mirror of https://github.com/ethereum/go-ethereum
Merge pull request #2819 from fjl/rpc-back-to-npipe
Godeps, rpc: switch back to package npipepull/2815/merge
commit
b7caa1751c
@ -1,22 +0,0 @@ |
|||||||
The MIT License (MIT) |
|
||||||
|
|
||||||
Copyright (c) 2015 Microsoft |
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|
||||||
of this software and associated documentation files (the "Software"), to deal |
|
||||||
in the Software without restriction, including without limitation the rights |
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
||||||
copies of the Software, and to permit persons to whom the Software is |
|
||||||
furnished to do so, subject to the following conditions: |
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all |
|
||||||
copies or substantial portions of the Software. |
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|
||||||
SOFTWARE. |
|
||||||
|
|
@ -1,15 +0,0 @@ |
|||||||
# go-winio |
|
||||||
|
|
||||||
This repository contains utilities for efficiently performing Win32 IO operations in |
|
||||||
Go. Currently, this is focused on accessing named pipes and other file handles, and |
|
||||||
for using named pipes as a net transport. |
|
||||||
|
|
||||||
This code relies on IO completion ports to avoid blocking IO on system threads, allowing Go |
|
||||||
to reuse the thread to schedule another goroutine. This limits support to Windows Vista and |
|
||||||
newer operating systems. This is similar to the implementation of network sockets in Go's net |
|
||||||
package. |
|
||||||
|
|
||||||
Please see the LICENSE file for licensing information. |
|
||||||
|
|
||||||
Thanks to natefinch for the inspiration for this library. See https://github.com/natefinch/npipe |
|
||||||
for another named pipe implementation. |
|
@ -1,266 +0,0 @@ |
|||||||
package winio |
|
||||||
|
|
||||||
import ( |
|
||||||
"encoding/binary" |
|
||||||
"errors" |
|
||||||
"fmt" |
|
||||||
"io" |
|
||||||
"io/ioutil" |
|
||||||
"os" |
|
||||||
"runtime" |
|
||||||
"syscall" |
|
||||||
"unicode/utf16" |
|
||||||
) |
|
||||||
|
|
||||||
//sys backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
|
|
||||||
//sys backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite
|
|
||||||
|
|
||||||
const ( |
|
||||||
BackupData = uint32(iota + 1) |
|
||||||
BackupEaData |
|
||||||
BackupSecurity |
|
||||||
BackupAlternateData |
|
||||||
BackupLink |
|
||||||
BackupPropertyData |
|
||||||
BackupObjectId |
|
||||||
BackupReparseData |
|
||||||
BackupSparseBlock |
|
||||||
BackupTxfsData |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
StreamSparseAttributes = uint32(8) |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
WRITE_DAC = 0x40000 |
|
||||||
WRITE_OWNER = 0x80000 |
|
||||||
ACCESS_SYSTEM_SECURITY = 0x1000000 |
|
||||||
) |
|
||||||
|
|
||||||
// BackupHeader represents a backup stream of a file.
|
|
||||||
type BackupHeader struct { |
|
||||||
Id uint32 // The backup stream ID
|
|
||||||
Attributes uint32 // Stream attributes
|
|
||||||
Size int64 // The size of the stream in bytes
|
|
||||||
Name string // The name of the stream (for BackupAlternateData only).
|
|
||||||
Offset int64 // The offset of the stream in the file (for BackupSparseBlock only).
|
|
||||||
} |
|
||||||
|
|
||||||
type win32StreamId struct { |
|
||||||
StreamId uint32 |
|
||||||
Attributes uint32 |
|
||||||
Size uint64 |
|
||||||
NameSize uint32 |
|
||||||
} |
|
||||||
|
|
||||||
// BackupStreamReader reads from a stream produced by the BackupRead Win32 API and produces a series
|
|
||||||
// of BackupHeader values.
|
|
||||||
type BackupStreamReader struct { |
|
||||||
r io.Reader |
|
||||||
bytesLeft int64 |
|
||||||
} |
|
||||||
|
|
||||||
// NewBackupStreamReader produces a BackupStreamReader from any io.Reader.
|
|
||||||
func NewBackupStreamReader(r io.Reader) *BackupStreamReader { |
|
||||||
return &BackupStreamReader{r, 0} |
|
||||||
} |
|
||||||
|
|
||||||
// Next returns the next backup stream and prepares for calls to Write(). It skips the remainder of the current stream if
|
|
||||||
// it was not completely read.
|
|
||||||
func (r *BackupStreamReader) Next() (*BackupHeader, error) { |
|
||||||
if r.bytesLeft > 0 { |
|
||||||
if _, err := io.Copy(ioutil.Discard, r); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
} |
|
||||||
var wsi win32StreamId |
|
||||||
if err := binary.Read(r.r, binary.LittleEndian, &wsi); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
hdr := &BackupHeader{ |
|
||||||
Id: wsi.StreamId, |
|
||||||
Attributes: wsi.Attributes, |
|
||||||
Size: int64(wsi.Size), |
|
||||||
} |
|
||||||
if wsi.NameSize != 0 { |
|
||||||
name := make([]uint16, int(wsi.NameSize/2)) |
|
||||||
if err := binary.Read(r.r, binary.LittleEndian, name); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
hdr.Name = syscall.UTF16ToString(name) |
|
||||||
} |
|
||||||
if wsi.StreamId == BackupSparseBlock { |
|
||||||
if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
hdr.Size -= 8 |
|
||||||
} |
|
||||||
r.bytesLeft = hdr.Size |
|
||||||
return hdr, nil |
|
||||||
} |
|
||||||
|
|
||||||
// Read reads from the current backup stream.
|
|
||||||
func (r *BackupStreamReader) Read(b []byte) (int, error) { |
|
||||||
if r.bytesLeft == 0 { |
|
||||||
return 0, io.EOF |
|
||||||
} |
|
||||||
if int64(len(b)) > r.bytesLeft { |
|
||||||
b = b[:r.bytesLeft] |
|
||||||
} |
|
||||||
n, err := r.r.Read(b) |
|
||||||
r.bytesLeft -= int64(n) |
|
||||||
if err == io.EOF { |
|
||||||
err = io.ErrUnexpectedEOF |
|
||||||
} else if r.bytesLeft == 0 && err == nil { |
|
||||||
err = io.EOF |
|
||||||
} |
|
||||||
return n, err |
|
||||||
} |
|
||||||
|
|
||||||
// BackupStreamWriter writes a stream compatible with the BackupWrite Win32 API.
|
|
||||||
type BackupStreamWriter struct { |
|
||||||
w io.Writer |
|
||||||
bytesLeft int64 |
|
||||||
} |
|
||||||
|
|
||||||
// NewBackupStreamWriter produces a BackupStreamWriter on top of an io.Writer.
|
|
||||||
func NewBackupStreamWriter(w io.Writer) *BackupStreamWriter { |
|
||||||
return &BackupStreamWriter{w, 0} |
|
||||||
} |
|
||||||
|
|
||||||
// WriteHeader writes the next backup stream header and prepares for calls to Write().
|
|
||||||
func (w *BackupStreamWriter) WriteHeader(hdr *BackupHeader) error { |
|
||||||
if w.bytesLeft != 0 { |
|
||||||
return fmt.Errorf("missing %d bytes", w.bytesLeft) |
|
||||||
} |
|
||||||
name := utf16.Encode([]rune(hdr.Name)) |
|
||||||
wsi := win32StreamId{ |
|
||||||
StreamId: hdr.Id, |
|
||||||
Attributes: hdr.Attributes, |
|
||||||
Size: uint64(hdr.Size), |
|
||||||
NameSize: uint32(len(name) * 2), |
|
||||||
} |
|
||||||
if hdr.Id == BackupSparseBlock { |
|
||||||
// Include space for the int64 block offset
|
|
||||||
wsi.Size += 8 |
|
||||||
} |
|
||||||
if err := binary.Write(w.w, binary.LittleEndian, &wsi); err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
if len(name) != 0 { |
|
||||||
if err := binary.Write(w.w, binary.LittleEndian, name); err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
} |
|
||||||
if hdr.Id == BackupSparseBlock { |
|
||||||
if err := binary.Write(w.w, binary.LittleEndian, hdr.Offset); err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
} |
|
||||||
w.bytesLeft = hdr.Size |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
// Write writes to the current backup stream.
|
|
||||||
func (w *BackupStreamWriter) Write(b []byte) (int, error) { |
|
||||||
if w.bytesLeft < int64(len(b)) { |
|
||||||
return 0, fmt.Errorf("too many bytes by %d", int64(len(b))-w.bytesLeft) |
|
||||||
} |
|
||||||
n, err := w.w.Write(b) |
|
||||||
w.bytesLeft -= int64(n) |
|
||||||
return n, err |
|
||||||
} |
|
||||||
|
|
||||||
// BackupFileReader provides an io.ReadCloser interface on top of the BackupRead Win32 API.
|
|
||||||
type BackupFileReader struct { |
|
||||||
f *os.File |
|
||||||
includeSecurity bool |
|
||||||
ctx uintptr |
|
||||||
} |
|
||||||
|
|
||||||
// NewBackupFileReader returns a new BackupFileReader from a file handle. If includeSecurity is true,
|
|
||||||
// Read will attempt to read the security descriptor of the file.
|
|
||||||
func NewBackupFileReader(f *os.File, includeSecurity bool) *BackupFileReader { |
|
||||||
r := &BackupFileReader{f, includeSecurity, 0} |
|
||||||
runtime.SetFinalizer(r, func(r *BackupFileReader) { r.Close() }) |
|
||||||
return r |
|
||||||
} |
|
||||||
|
|
||||||
// Read reads a backup stream from the file by calling the Win32 API BackupRead().
|
|
||||||
func (r *BackupFileReader) Read(b []byte) (int, error) { |
|
||||||
var bytesRead uint32 |
|
||||||
err := backupRead(syscall.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx) |
|
||||||
if err != nil { |
|
||||||
return 0, &os.PathError{"BackupRead", r.f.Name(), err} |
|
||||||
} |
|
||||||
if bytesRead == 0 { |
|
||||||
return 0, io.EOF |
|
||||||
} |
|
||||||
return int(bytesRead), nil |
|
||||||
} |
|
||||||
|
|
||||||
// Close frees Win32 resources associated with the BackupFileReader. It does not close
|
|
||||||
// the underlying file.
|
|
||||||
func (r *BackupFileReader) Close() error { |
|
||||||
if r.ctx != 0 { |
|
||||||
backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx) |
|
||||||
r.ctx = 0 |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
// BackupFileWriter provides an io.WriteCloser interface on top of the BackupWrite Win32 API.
|
|
||||||
type BackupFileWriter struct { |
|
||||||
f *os.File |
|
||||||
includeSecurity bool |
|
||||||
ctx uintptr |
|
||||||
} |
|
||||||
|
|
||||||
// NewBackupFileWrtier returns a new BackupFileWriter from a file handle. If includeSecurity is true,
|
|
||||||
// Write() will attempt to restore the security descriptor from the stream.
|
|
||||||
func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter { |
|
||||||
w := &BackupFileWriter{f, includeSecurity, 0} |
|
||||||
runtime.SetFinalizer(w, func(w *BackupFileWriter) { w.Close() }) |
|
||||||
return w |
|
||||||
} |
|
||||||
|
|
||||||
// Write restores a portion of the file using the provided backup stream.
|
|
||||||
func (w *BackupFileWriter) Write(b []byte) (int, error) { |
|
||||||
var bytesWritten uint32 |
|
||||||
err := backupWrite(syscall.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx) |
|
||||||
if err != nil { |
|
||||||
return 0, &os.PathError{"BackupWrite", w.f.Name(), err} |
|
||||||
} |
|
||||||
if int(bytesWritten) != len(b) { |
|
||||||
return int(bytesWritten), errors.New("not all bytes could be written") |
|
||||||
} |
|
||||||
return len(b), nil |
|
||||||
} |
|
||||||
|
|
||||||
// Close frees Win32 resources associated with the BackupFileWriter. It does not
|
|
||||||
// close the underlying file.
|
|
||||||
func (w *BackupFileWriter) Close() error { |
|
||||||
if w.ctx != 0 { |
|
||||||
backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx) |
|
||||||
w.ctx = 0 |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
// OpenForBackup opens a file or directory, potentially skipping access checks if the backup
|
|
||||||
// or restore privileges have been acquired.
|
|
||||||
//
|
|
||||||
// If the file opened was a directory, it cannot be used with Readdir().
|
|
||||||
func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) { |
|
||||||
winPath, err := syscall.UTF16FromString(path) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
h, err := syscall.CreateFile(&winPath[0], access, share, nil, createmode, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0) |
|
||||||
if err != nil { |
|
||||||
err = &os.PathError{Op: "open", Path: path, Err: err} |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
return os.NewFile(uintptr(h), path), nil |
|
||||||
} |
|
@ -1,219 +0,0 @@ |
|||||||
package winio |
|
||||||
|
|
||||||
import ( |
|
||||||
"errors" |
|
||||||
"io" |
|
||||||
"runtime" |
|
||||||
"sync" |
|
||||||
"syscall" |
|
||||||
"time" |
|
||||||
) |
|
||||||
|
|
||||||
//sys cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) = CancelIoEx
|
|
||||||
//sys createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) = CreateIoCompletionPort
|
|
||||||
//sys getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
|
|
||||||
//sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
|
|
||||||
//sys timeBeginPeriod(period uint32) (n int32) = winmm.timeBeginPeriod
|
|
||||||
|
|
||||||
const ( |
|
||||||
cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1 |
|
||||||
cFILE_SKIP_SET_EVENT_ON_HANDLE = 2 |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
ErrFileClosed = errors.New("file has already been closed") |
|
||||||
ErrTimeout = &timeoutError{} |
|
||||||
) |
|
||||||
|
|
||||||
type timeoutError struct{} |
|
||||||
|
|
||||||
func (e *timeoutError) Error() string { return "i/o timeout" } |
|
||||||
func (e *timeoutError) Timeout() bool { return true } |
|
||||||
func (e *timeoutError) Temporary() bool { return true } |
|
||||||
|
|
||||||
var ioInitOnce sync.Once |
|
||||||
var ioCompletionPort syscall.Handle |
|
||||||
|
|
||||||
// ioResult contains the result of an asynchronous IO operation
|
|
||||||
type ioResult struct { |
|
||||||
bytes uint32 |
|
||||||
err error |
|
||||||
} |
|
||||||
|
|
||||||
// ioOperation represents an outstanding asynchronous Win32 IO
|
|
||||||
type ioOperation struct { |
|
||||||
o syscall.Overlapped |
|
||||||
ch chan ioResult |
|
||||||
} |
|
||||||
|
|
||||||
func initIo() { |
|
||||||
h, err := createIoCompletionPort(syscall.InvalidHandle, 0, 0, 0xffffffff) |
|
||||||
if err != nil { |
|
||||||
panic(err) |
|
||||||
} |
|
||||||
ioCompletionPort = h |
|
||||||
go ioCompletionProcessor(h) |
|
||||||
} |
|
||||||
|
|
||||||
// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
|
|
||||||
// It takes ownership of this handle and will close it if it is garbage collected.
|
|
||||||
type win32File struct { |
|
||||||
handle syscall.Handle |
|
||||||
wg sync.WaitGroup |
|
||||||
closing bool |
|
||||||
readDeadline time.Time |
|
||||||
writeDeadline time.Time |
|
||||||
} |
|
||||||
|
|
||||||
// makeWin32File makes a new win32File from an existing file handle
|
|
||||||
func makeWin32File(h syscall.Handle) (*win32File, error) { |
|
||||||
f := &win32File{handle: h} |
|
||||||
ioInitOnce.Do(initIo) |
|
||||||
_, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
err = setFileCompletionNotificationModes(h, cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS|cFILE_SKIP_SET_EVENT_ON_HANDLE) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
runtime.SetFinalizer(f, (*win32File).closeHandle) |
|
||||||
return f, nil |
|
||||||
} |
|
||||||
|
|
||||||
func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) { |
|
||||||
return makeWin32File(h) |
|
||||||
} |
|
||||||
|
|
||||||
// closeHandle closes the resources associated with a Win32 handle
|
|
||||||
func (f *win32File) closeHandle() { |
|
||||||
if !f.closing { |
|
||||||
// cancel all IO and wait for it to complete
|
|
||||||
f.closing = true |
|
||||||
cancelIoEx(f.handle, nil) |
|
||||||
f.wg.Wait() |
|
||||||
// at this point, no new IO can start
|
|
||||||
syscall.Close(f.handle) |
|
||||||
f.handle = 0 |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Close closes a win32File.
|
|
||||||
func (f *win32File) Close() error { |
|
||||||
f.closeHandle() |
|
||||||
runtime.SetFinalizer(f, nil) |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
// prepareIo prepares for a new IO operation
|
|
||||||
func (f *win32File) prepareIo() (*ioOperation, error) { |
|
||||||
f.wg.Add(1) |
|
||||||
if f.closing { |
|
||||||
return nil, ErrFileClosed |
|
||||||
} |
|
||||||
c := &ioOperation{} |
|
||||||
c.ch = make(chan ioResult) |
|
||||||
return c, nil |
|
||||||
} |
|
||||||
|
|
||||||
// ioCompletionProcessor processes completed async IOs forever
|
|
||||||
func ioCompletionProcessor(h syscall.Handle) { |
|
||||||
// Set the timer resolution to 1. This fixes a performance regression in golang 1.6.
|
|
||||||
timeBeginPeriod(1) |
|
||||||
for { |
|
||||||
var bytes uint32 |
|
||||||
var key uintptr |
|
||||||
var op *ioOperation |
|
||||||
err := getQueuedCompletionStatus(h, &bytes, &key, &op, syscall.INFINITE) |
|
||||||
if op == nil { |
|
||||||
panic(err) |
|
||||||
} |
|
||||||
op.ch <- ioResult{bytes, err} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// asyncIo processes the return value from ReadFile or WriteFile, blocking until
|
|
||||||
// the operation has actually completed.
|
|
||||||
func (f *win32File) asyncIo(c *ioOperation, deadline time.Time, bytes uint32, err error) (int, error) { |
|
||||||
if err != syscall.ERROR_IO_PENDING { |
|
||||||
f.wg.Done() |
|
||||||
return int(bytes), err |
|
||||||
} else { |
|
||||||
var r ioResult |
|
||||||
wait := true |
|
||||||
timedout := false |
|
||||||
if f.closing { |
|
||||||
cancelIoEx(f.handle, &c.o) |
|
||||||
} else if !deadline.IsZero() { |
|
||||||
now := time.Now() |
|
||||||
if !deadline.After(now) { |
|
||||||
timedout = true |
|
||||||
} else { |
|
||||||
timeout := time.After(deadline.Sub(now)) |
|
||||||
select { |
|
||||||
case r = <-c.ch: |
|
||||||
wait = false |
|
||||||
case <-timeout: |
|
||||||
timedout = true |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
if timedout { |
|
||||||
cancelIoEx(f.handle, &c.o) |
|
||||||
} |
|
||||||
if wait { |
|
||||||
r = <-c.ch |
|
||||||
} |
|
||||||
err = r.err |
|
||||||
if err == syscall.ERROR_OPERATION_ABORTED { |
|
||||||
if f.closing { |
|
||||||
err = ErrFileClosed |
|
||||||
} else if timedout { |
|
||||||
err = ErrTimeout |
|
||||||
} |
|
||||||
} |
|
||||||
f.wg.Done() |
|
||||||
return int(r.bytes), err |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Read reads from a file handle.
|
|
||||||
func (f *win32File) Read(b []byte) (int, error) { |
|
||||||
c, err := f.prepareIo() |
|
||||||
if err != nil { |
|
||||||
return 0, err |
|
||||||
} |
|
||||||
var bytes uint32 |
|
||||||
err = syscall.ReadFile(f.handle, b, &bytes, &c.o) |
|
||||||
n, err := f.asyncIo(c, f.readDeadline, bytes, err) |
|
||||||
|
|
||||||
// Handle EOF conditions.
|
|
||||||
if err == nil && n == 0 && len(b) != 0 { |
|
||||||
return 0, io.EOF |
|
||||||
} else if err == syscall.ERROR_BROKEN_PIPE { |
|
||||||
return 0, io.EOF |
|
||||||
} else { |
|
||||||
return n, err |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Write writes to a file handle.
|
|
||||||
func (f *win32File) Write(b []byte) (int, error) { |
|
||||||
c, err := f.prepareIo() |
|
||||||
if err != nil { |
|
||||||
return 0, err |
|
||||||
} |
|
||||||
var bytes uint32 |
|
||||||
err = syscall.WriteFile(f.handle, b, &bytes, &c.o) |
|
||||||
return f.asyncIo(c, f.writeDeadline, bytes, err) |
|
||||||
} |
|
||||||
|
|
||||||
func (f *win32File) SetReadDeadline(t time.Time) error { |
|
||||||
f.readDeadline = t |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (f *win32File) SetWriteDeadline(t time.Time) error { |
|
||||||
f.writeDeadline = t |
|
||||||
return nil |
|
||||||
} |
|
@ -1,54 +0,0 @@ |
|||||||
package winio |
|
||||||
|
|
||||||
import ( |
|
||||||
"os" |
|
||||||
"syscall" |
|
||||||
"unsafe" |
|
||||||
) |
|
||||||
|
|
||||||
//sys getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = GetFileInformationByHandleEx
|
|
||||||
//sys setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = SetFileInformationByHandle
|
|
||||||
|
|
||||||
const ( |
|
||||||
fileBasicInfo = 0 |
|
||||||
fileIDInfo = 0x12 |
|
||||||
) |
|
||||||
|
|
||||||
// FileBasicInfo contains file access time and file attributes information.
|
|
||||||
type FileBasicInfo struct { |
|
||||||
CreationTime, LastAccessTime, LastWriteTime, ChangeTime syscall.Filetime |
|
||||||
FileAttributes uintptr // includes padding
|
|
||||||
} |
|
||||||
|
|
||||||
// GetFileBasicInfo retrieves times and attributes for a file.
|
|
||||||
func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) { |
|
||||||
bi := &FileBasicInfo{} |
|
||||||
if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil { |
|
||||||
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} |
|
||||||
} |
|
||||||
return bi, nil |
|
||||||
} |
|
||||||
|
|
||||||
// SetFileBasicInfo sets times and attributes for a file.
|
|
||||||
func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error { |
|
||||||
if err := setFileInformationByHandle(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil { |
|
||||||
return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err} |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
// FileIDInfo contains the volume serial number and file ID for a file. This pair should be
|
|
||||||
// unique on a system.
|
|
||||||
type FileIDInfo struct { |
|
||||||
VolumeSerialNumber uint64 |
|
||||||
FileID [16]byte |
|
||||||
} |
|
||||||
|
|
||||||
// GetFileID retrieves the unique (volume, file ID) pair for a file.
|
|
||||||
func GetFileID(f *os.File) (*FileIDInfo, error) { |
|
||||||
fileID := &FileIDInfo{} |
|
||||||
if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileIDInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil { |
|
||||||
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} |
|
||||||
} |
|
||||||
return fileID, nil |
|
||||||
} |
|
@ -1,398 +0,0 @@ |
|||||||
package winio |
|
||||||
|
|
||||||
import ( |
|
||||||
"errors" |
|
||||||
"io" |
|
||||||
"net" |
|
||||||
"os" |
|
||||||
"syscall" |
|
||||||
"time" |
|
||||||
"unsafe" |
|
||||||
) |
|
||||||
|
|
||||||
//sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe
|
|
||||||
//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *securityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW
|
|
||||||
//sys createFile(name string, access uint32, mode uint32, sa *securityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateFileW
|
|
||||||
//sys waitNamedPipe(name string, timeout uint32) (err error) = WaitNamedPipeW
|
|
||||||
//sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
|
|
||||||
//sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
|
|
||||||
|
|
||||||
type securityAttributes struct { |
|
||||||
Length uint32 |
|
||||||
SecurityDescriptor *byte |
|
||||||
InheritHandle uint32 |
|
||||||
} |
|
||||||
|
|
||||||
const ( |
|
||||||
cERROR_PIPE_BUSY = syscall.Errno(231) |
|
||||||
cERROR_PIPE_CONNECTED = syscall.Errno(535) |
|
||||||
cERROR_SEM_TIMEOUT = syscall.Errno(121) |
|
||||||
|
|
||||||
cPIPE_ACCESS_DUPLEX = 0x3 |
|
||||||
cFILE_FLAG_FIRST_PIPE_INSTANCE = 0x80000 |
|
||||||
cSECURITY_SQOS_PRESENT = 0x100000 |
|
||||||
cSECURITY_ANONYMOUS = 0 |
|
||||||
|
|
||||||
cPIPE_REJECT_REMOTE_CLIENTS = 0x8 |
|
||||||
|
|
||||||
cPIPE_UNLIMITED_INSTANCES = 255 |
|
||||||
|
|
||||||
cNMPWAIT_USE_DEFAULT_WAIT = 0 |
|
||||||
cNMPWAIT_NOWAIT = 1 |
|
||||||
|
|
||||||
cPIPE_TYPE_MESSAGE = 4 |
|
||||||
|
|
||||||
cPIPE_READMODE_MESSAGE = 2 |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
// ErrPipeListenerClosed is returned for pipe operations on listeners that have been closed.
|
|
||||||
// This error should match net.errClosing since docker takes a dependency on its text.
|
|
||||||
ErrPipeListenerClosed = errors.New("use of closed network connection") |
|
||||||
|
|
||||||
errPipeWriteClosed = errors.New("pipe has been closed for write") |
|
||||||
) |
|
||||||
|
|
||||||
type win32Pipe struct { |
|
||||||
*win32File |
|
||||||
path string |
|
||||||
} |
|
||||||
|
|
||||||
type win32MessageBytePipe struct { |
|
||||||
win32Pipe |
|
||||||
writeClosed bool |
|
||||||
readEOF bool |
|
||||||
} |
|
||||||
|
|
||||||
type pipeAddress string |
|
||||||
|
|
||||||
func (f *win32Pipe) LocalAddr() net.Addr { |
|
||||||
return pipeAddress(f.path) |
|
||||||
} |
|
||||||
|
|
||||||
func (f *win32Pipe) RemoteAddr() net.Addr { |
|
||||||
return pipeAddress(f.path) |
|
||||||
} |
|
||||||
|
|
||||||
func (f *win32Pipe) SetDeadline(t time.Time) error { |
|
||||||
f.SetReadDeadline(t) |
|
||||||
f.SetWriteDeadline(t) |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
// CloseWrite closes the write side of a message pipe in byte mode.
|
|
||||||
func (f *win32MessageBytePipe) CloseWrite() error { |
|
||||||
if f.writeClosed { |
|
||||||
return errPipeWriteClosed |
|
||||||
} |
|
||||||
_, err := f.win32File.Write(nil) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
f.writeClosed = true |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
// Write writes bytes to a message pipe in byte mode. Zero-byte writes are ignored, since
|
|
||||||
// they are used to implement CloseWrite().
|
|
||||||
func (f *win32MessageBytePipe) Write(b []byte) (int, error) { |
|
||||||
if f.writeClosed { |
|
||||||
return 0, errPipeWriteClosed |
|
||||||
} |
|
||||||
if len(b) == 0 { |
|
||||||
return 0, nil |
|
||||||
} |
|
||||||
return f.win32File.Write(b) |
|
||||||
} |
|
||||||
|
|
||||||
// Read reads bytes from a message pipe in byte mode. A read of a zero-byte message on a message
|
|
||||||
// mode pipe will return io.EOF, as will all subsequent reads.
|
|
||||||
func (f *win32MessageBytePipe) Read(b []byte) (int, error) { |
|
||||||
if f.readEOF { |
|
||||||
return 0, io.EOF |
|
||||||
} |
|
||||||
n, err := f.win32File.Read(b) |
|
||||||
if err == io.EOF { |
|
||||||
// If this was the result of a zero-byte read, then
|
|
||||||
// it is possible that the read was due to a zero-size
|
|
||||||
// message. Since we are simulating CloseWrite with a
|
|
||||||
// zero-byte message, ensure that all future Read() calls
|
|
||||||
// also return EOF.
|
|
||||||
f.readEOF = true |
|
||||||
} |
|
||||||
return n, err |
|
||||||
} |
|
||||||
|
|
||||||
func (s pipeAddress) Network() string { |
|
||||||
return "pipe" |
|
||||||
} |
|
||||||
|
|
||||||
func (s pipeAddress) String() string { |
|
||||||
return string(s) |
|
||||||
} |
|
||||||
|
|
||||||
// DialPipe connects to a named pipe by path, timing out if the connection
|
|
||||||
// takes longer than the specified duration. If timeout is nil, then the timeout
|
|
||||||
// is the default timeout established by the pipe server.
|
|
||||||
func DialPipe(path string, timeout *time.Duration) (net.Conn, error) { |
|
||||||
var absTimeout time.Time |
|
||||||
if timeout != nil { |
|
||||||
absTimeout = time.Now().Add(*timeout) |
|
||||||
} |
|
||||||
var err error |
|
||||||
var h syscall.Handle |
|
||||||
for { |
|
||||||
h, err = createFile(path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0) |
|
||||||
if err != cERROR_PIPE_BUSY { |
|
||||||
break |
|
||||||
} |
|
||||||
now := time.Now() |
|
||||||
var ms uint32 |
|
||||||
if absTimeout.IsZero() { |
|
||||||
ms = cNMPWAIT_USE_DEFAULT_WAIT |
|
||||||
} else if now.After(absTimeout) { |
|
||||||
ms = cNMPWAIT_NOWAIT |
|
||||||
} else { |
|
||||||
ms = uint32(absTimeout.Sub(now).Nanoseconds() / 1000 / 1000) |
|
||||||
} |
|
||||||
err = waitNamedPipe(path, ms) |
|
||||||
if err != nil { |
|
||||||
if err == cERROR_SEM_TIMEOUT { |
|
||||||
return nil, ErrTimeout |
|
||||||
} |
|
||||||
break |
|
||||||
} |
|
||||||
} |
|
||||||
if err != nil { |
|
||||||
return nil, &os.PathError{Op: "open", Path: path, Err: err} |
|
||||||
} |
|
||||||
|
|
||||||
var flags uint32 |
|
||||||
err = getNamedPipeInfo(h, &flags, nil, nil, nil) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
var state uint32 |
|
||||||
err = getNamedPipeHandleState(h, &state, nil, nil, nil, nil, 0) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
if state&cPIPE_READMODE_MESSAGE != 0 { |
|
||||||
return nil, &os.PathError{Op: "open", Path: path, Err: errors.New("message readmode pipes not supported")} |
|
||||||
} |
|
||||||
|
|
||||||
f, err := makeWin32File(h) |
|
||||||
if err != nil { |
|
||||||
syscall.Close(h) |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
// If the pipe is in message mode, return a message byte pipe, which
|
|
||||||
// supports CloseWrite().
|
|
||||||
if flags&cPIPE_TYPE_MESSAGE != 0 { |
|
||||||
return &win32MessageBytePipe{ |
|
||||||
win32Pipe: win32Pipe{win32File: f, path: path}, |
|
||||||
}, nil |
|
||||||
} |
|
||||||
return &win32Pipe{win32File: f, path: path}, nil |
|
||||||
} |
|
||||||
|
|
||||||
type acceptResponse struct { |
|
||||||
f *win32File |
|
||||||
err error |
|
||||||
} |
|
||||||
|
|
||||||
type win32PipeListener struct { |
|
||||||
firstHandle syscall.Handle |
|
||||||
path string |
|
||||||
securityDescriptor []byte |
|
||||||
config PipeConfig |
|
||||||
acceptCh chan (chan acceptResponse) |
|
||||||
closeCh chan int |
|
||||||
doneCh chan int |
|
||||||
} |
|
||||||
|
|
||||||
func makeServerPipeHandle(path string, securityDescriptor []byte, c *PipeConfig, first bool) (syscall.Handle, error) { |
|
||||||
var flags uint32 = cPIPE_ACCESS_DUPLEX | syscall.FILE_FLAG_OVERLAPPED |
|
||||||
if first { |
|
||||||
flags |= cFILE_FLAG_FIRST_PIPE_INSTANCE |
|
||||||
} |
|
||||||
|
|
||||||
var mode uint32 = cPIPE_REJECT_REMOTE_CLIENTS |
|
||||||
if c.MessageMode { |
|
||||||
mode |= cPIPE_TYPE_MESSAGE |
|
||||||
} |
|
||||||
|
|
||||||
var sa securityAttributes |
|
||||||
sa.Length = uint32(unsafe.Sizeof(sa)) |
|
||||||
if securityDescriptor != nil { |
|
||||||
sa.SecurityDescriptor = &securityDescriptor[0] |
|
||||||
} |
|
||||||
h, err := createNamedPipe(path, flags, mode, cPIPE_UNLIMITED_INSTANCES, uint32(c.OutputBufferSize), uint32(c.InputBufferSize), 0, &sa) |
|
||||||
if err != nil { |
|
||||||
return 0, &os.PathError{Op: "open", Path: path, Err: err} |
|
||||||
} |
|
||||||
return h, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (l *win32PipeListener) makeServerPipe() (*win32File, error) { |
|
||||||
h, err := makeServerPipeHandle(l.path, l.securityDescriptor, &l.config, false) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
f, err := makeWin32File(h) |
|
||||||
if err != nil { |
|
||||||
syscall.Close(h) |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
return f, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (l *win32PipeListener) listenerRoutine() { |
|
||||||
closed := false |
|
||||||
for !closed { |
|
||||||
select { |
|
||||||
case <-l.closeCh: |
|
||||||
closed = true |
|
||||||
case responseCh := <-l.acceptCh: |
|
||||||
p, err := l.makeServerPipe() |
|
||||||
if err == nil { |
|
||||||
// Wait for the client to connect.
|
|
||||||
ch := make(chan error) |
|
||||||
go func() { |
|
||||||
ch <- connectPipe(p) |
|
||||||
}() |
|
||||||
select { |
|
||||||
case err = <-ch: |
|
||||||
if err != nil { |
|
||||||
p.Close() |
|
||||||
p = nil |
|
||||||
} |
|
||||||
case <-l.closeCh: |
|
||||||
// Abort the connect request by closing the handle.
|
|
||||||
p.Close() |
|
||||||
p = nil |
|
||||||
err = <-ch |
|
||||||
if err == nil || err == ErrFileClosed { |
|
||||||
err = ErrPipeListenerClosed |
|
||||||
} |
|
||||||
closed = true |
|
||||||
} |
|
||||||
} |
|
||||||
responseCh <- acceptResponse{p, err} |
|
||||||
} |
|
||||||
} |
|
||||||
syscall.Close(l.firstHandle) |
|
||||||
l.firstHandle = 0 |
|
||||||
// Notify Close() and Accept() callers that the handle has been closed.
|
|
||||||
close(l.doneCh) |
|
||||||
} |
|
||||||
|
|
||||||
// PipeConfig contain configuration for the pipe listener.
|
|
||||||
type PipeConfig struct { |
|
||||||
// SecurityDescriptor contains a Windows security descriptor in SDDL format.
|
|
||||||
SecurityDescriptor string |
|
||||||
|
|
||||||
// MessageMode determines whether the pipe is in byte or message mode. In either
|
|
||||||
// case the pipe is read in byte mode by default. The only practical difference in
|
|
||||||
// this implementation is that CloseWrite() is only supported for message mode pipes;
|
|
||||||
// CloseWrite() is implemented as a zero-byte write, but zero-byte writes are only
|
|
||||||
// transferred to the reader (and returned as io.EOF in this implementation)
|
|
||||||
// when the pipe is in message mode.
|
|
||||||
MessageMode bool |
|
||||||
|
|
||||||
// InputBufferSize specifies the size the input buffer, in bytes.
|
|
||||||
InputBufferSize int32 |
|
||||||
|
|
||||||
// OutputBufferSize specifies the size the input buffer, in bytes.
|
|
||||||
OutputBufferSize int32 |
|
||||||
} |
|
||||||
|
|
||||||
// ListenPipe creates a listener on a Windows named pipe path, e.g. \\.\pipe\mypipe.
|
|
||||||
// The pipe must not already exist.
|
|
||||||
func ListenPipe(path string, c *PipeConfig) (net.Listener, error) { |
|
||||||
var ( |
|
||||||
sd []byte |
|
||||||
err error |
|
||||||
) |
|
||||||
if c == nil { |
|
||||||
c = &PipeConfig{} |
|
||||||
} |
|
||||||
if c.SecurityDescriptor != "" { |
|
||||||
sd, err = SddlToSecurityDescriptor(c.SecurityDescriptor) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
} |
|
||||||
h, err := makeServerPipeHandle(path, sd, c, true) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
// Immediately open and then close a client handle so that the named pipe is
|
|
||||||
// created but not currently accepting connections.
|
|
||||||
h2, err := createFile(path, 0, 0, nil, syscall.OPEN_EXISTING, cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0) |
|
||||||
if err != nil { |
|
||||||
syscall.Close(h) |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
syscall.Close(h2) |
|
||||||
l := &win32PipeListener{ |
|
||||||
firstHandle: h, |
|
||||||
path: path, |
|
||||||
securityDescriptor: sd, |
|
||||||
config: *c, |
|
||||||
acceptCh: make(chan (chan acceptResponse)), |
|
||||||
closeCh: make(chan int), |
|
||||||
doneCh: make(chan int), |
|
||||||
} |
|
||||||
go l.listenerRoutine() |
|
||||||
return l, nil |
|
||||||
} |
|
||||||
|
|
||||||
func connectPipe(p *win32File) error { |
|
||||||
c, err := p.prepareIo() |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
err = connectNamedPipe(p.handle, &c.o) |
|
||||||
_, err = p.asyncIo(c, time.Time{}, 0, err) |
|
||||||
if err != nil && err != cERROR_PIPE_CONNECTED { |
|
||||||
return err |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (l *win32PipeListener) Accept() (net.Conn, error) { |
|
||||||
ch := make(chan acceptResponse) |
|
||||||
select { |
|
||||||
case l.acceptCh <- ch: |
|
||||||
response := <-ch |
|
||||||
err := response.err |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
if l.config.MessageMode { |
|
||||||
return &win32MessageBytePipe{ |
|
||||||
win32Pipe: win32Pipe{win32File: response.f, path: l.path}, |
|
||||||
}, nil |
|
||||||
} |
|
||||||
return &win32Pipe{win32File: response.f, path: l.path}, nil |
|
||||||
case <-l.doneCh: |
|
||||||
return nil, ErrPipeListenerClosed |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (l *win32PipeListener) Close() error { |
|
||||||
select { |
|
||||||
case l.closeCh <- 1: |
|
||||||
<-l.doneCh |
|
||||||
case <-l.doneCh: |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (l *win32PipeListener) Addr() net.Addr { |
|
||||||
return pipeAddress(l.path) |
|
||||||
} |
|
@ -1,150 +0,0 @@ |
|||||||
package winio |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"encoding/binary" |
|
||||||
"fmt" |
|
||||||
"runtime" |
|
||||||
"syscall" |
|
||||||
"unicode/utf16" |
|
||||||
) |
|
||||||
|
|
||||||
//sys adjustTokenPrivileges(token syscall.Handle, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges
|
|
||||||
//sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf
|
|
||||||
//sys revertToSelf() (err error) = advapi32.RevertToSelf
|
|
||||||
//sys openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *syscall.Handle) (err error) = advapi32.OpenThreadToken
|
|
||||||
//sys getCurrentThread() (h syscall.Handle) = GetCurrentThread
|
|
||||||
//sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW
|
|
||||||
//sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
|
|
||||||
//sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW
|
|
||||||
|
|
||||||
const ( |
|
||||||
SE_PRIVILEGE_ENABLED = 2 |
|
||||||
|
|
||||||
ERROR_NOT_ALL_ASSIGNED syscall.Errno = 1300 |
|
||||||
|
|
||||||
SeBackupPrivilege = "SeBackupPrivilege" |
|
||||||
SeRestorePrivilege = "SeRestorePrivilege" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
securityAnonymous = iota |
|
||||||
securityIdentification |
|
||||||
securityImpersonation |
|
||||||
securityDelegation |
|
||||||
) |
|
||||||
|
|
||||||
type PrivilegeError struct { |
|
||||||
privileges []uint64 |
|
||||||
} |
|
||||||
|
|
||||||
func (e *PrivilegeError) Error() string { |
|
||||||
s := "" |
|
||||||
if len(e.privileges) > 1 { |
|
||||||
s = "Could not enable privileges " |
|
||||||
} else { |
|
||||||
s = "Could not enable privilege " |
|
||||||
} |
|
||||||
for i, p := range e.privileges { |
|
||||||
if i != 0 { |
|
||||||
s += ", " |
|
||||||
} |
|
||||||
s += `"` |
|
||||||
s += getPrivilegeName(p) |
|
||||||
s += `"` |
|
||||||
} |
|
||||||
return s |
|
||||||
} |
|
||||||
|
|
||||||
func RunWithPrivilege(name string, fn func() error) error { |
|
||||||
return RunWithPrivileges([]string{name}, fn) |
|
||||||
} |
|
||||||
|
|
||||||
func RunWithPrivileges(names []string, fn func() error) error { |
|
||||||
var privileges []uint64 |
|
||||||
for _, name := range names { |
|
||||||
p := uint64(0) |
|
||||||
err := lookupPrivilegeValue("", name, &p) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
privileges = append(privileges, p) |
|
||||||
} |
|
||||||
runtime.LockOSThread() |
|
||||||
defer runtime.UnlockOSThread() |
|
||||||
token, err := newThreadToken() |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
defer releaseThreadToken(token) |
|
||||||
err = adjustPrivileges(token, privileges) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
return fn() |
|
||||||
} |
|
||||||
|
|
||||||
func adjustPrivileges(token syscall.Handle, privileges []uint64) error { |
|
||||||
var b bytes.Buffer |
|
||||||
binary.Write(&b, binary.LittleEndian, uint32(len(privileges))) |
|
||||||
for _, p := range privileges { |
|
||||||
binary.Write(&b, binary.LittleEndian, p) |
|
||||||
binary.Write(&b, binary.LittleEndian, uint32(SE_PRIVILEGE_ENABLED)) |
|
||||||
} |
|
||||||
prevState := make([]byte, b.Len()) |
|
||||||
reqSize := uint32(0) |
|
||||||
success, err := adjustTokenPrivileges(token, false, &b.Bytes()[0], uint32(len(prevState)), &prevState[0], &reqSize) |
|
||||||
if !success { |
|
||||||
return err |
|
||||||
} |
|
||||||
if err == ERROR_NOT_ALL_ASSIGNED { |
|
||||||
return &PrivilegeError{privileges} |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func getPrivilegeName(luid uint64) string { |
|
||||||
var nameBuffer [256]uint16 |
|
||||||
bufSize := uint32(len(nameBuffer)) |
|
||||||
err := lookupPrivilegeName("", &luid, &nameBuffer[0], &bufSize) |
|
||||||
if err != nil { |
|
||||||
return fmt.Sprintf("<unknown privilege %d>", luid) |
|
||||||
} |
|
||||||
|
|
||||||
var displayNameBuffer [256]uint16 |
|
||||||
displayBufSize := uint32(len(displayNameBuffer)) |
|
||||||
var langId uint32 |
|
||||||
err = lookupPrivilegeDisplayName("", &nameBuffer[0], &displayNameBuffer[0], &displayBufSize, &langId) |
|
||||||
if err != nil { |
|
||||||
return fmt.Sprintf("<unknown privilege %s>", utf16.Decode(nameBuffer[:bufSize])) |
|
||||||
} |
|
||||||
|
|
||||||
return string(utf16.Decode(displayNameBuffer[:displayBufSize])) |
|
||||||
} |
|
||||||
|
|
||||||
func newThreadToken() (syscall.Handle, error) { |
|
||||||
err := impersonateSelf(securityImpersonation) |
|
||||||
if err != nil { |
|
||||||
panic(err) |
|
||||||
return 0, err |
|
||||||
} |
|
||||||
|
|
||||||
var token syscall.Handle |
|
||||||
err = openThreadToken(getCurrentThread(), syscall.TOKEN_ADJUST_PRIVILEGES|syscall.TOKEN_QUERY, false, &token) |
|
||||||
if err != nil { |
|
||||||
rerr := revertToSelf() |
|
||||||
if rerr != nil { |
|
||||||
panic(rerr) |
|
||||||
} |
|
||||||
return 0, err |
|
||||||
} |
|
||||||
return token, nil |
|
||||||
} |
|
||||||
|
|
||||||
func releaseThreadToken(h syscall.Handle) { |
|
||||||
err := revertToSelf() |
|
||||||
if err != nil { |
|
||||||
panic(err) |
|
||||||
} |
|
||||||
syscall.Close(h) |
|
||||||
} |
|
@ -1,124 +0,0 @@ |
|||||||
package winio |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"encoding/binary" |
|
||||||
"fmt" |
|
||||||
"strings" |
|
||||||
"unicode/utf16" |
|
||||||
"unsafe" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
reparseTagMountPoint = 0xA0000003 |
|
||||||
reparseTagSymlink = 0xA000000C |
|
||||||
) |
|
||||||
|
|
||||||
type reparseDataBuffer struct { |
|
||||||
ReparseTag uint32 |
|
||||||
ReparseDataLength uint16 |
|
||||||
Reserved uint16 |
|
||||||
SubstituteNameOffset uint16 |
|
||||||
SubstituteNameLength uint16 |
|
||||||
PrintNameOffset uint16 |
|
||||||
PrintNameLength uint16 |
|
||||||
} |
|
||||||
|
|
||||||
// ReparsePoint describes a Win32 symlink or mount point.
|
|
||||||
type ReparsePoint struct { |
|
||||||
Target string |
|
||||||
IsMountPoint bool |
|
||||||
} |
|
||||||
|
|
||||||
// UnsupportedReparsePointError is returned when trying to decode a non-symlink or
|
|
||||||
// mount point reparse point.
|
|
||||||
type UnsupportedReparsePointError struct { |
|
||||||
Tag uint32 |
|
||||||
} |
|
||||||
|
|
||||||
func (e *UnsupportedReparsePointError) Error() string { |
|
||||||
return fmt.Sprintf("unsupported reparse point %x", e.Tag) |
|
||||||
} |
|
||||||
|
|
||||||
// DecodeReparsePoint decodes a Win32 REPARSE_DATA_BUFFER structure containing either a symlink
|
|
||||||
// or a mount point.
|
|
||||||
func DecodeReparsePoint(b []byte) (*ReparsePoint, error) { |
|
||||||
isMountPoint := false |
|
||||||
tag := binary.LittleEndian.Uint32(b[0:4]) |
|
||||||
switch tag { |
|
||||||
case reparseTagMountPoint: |
|
||||||
isMountPoint = true |
|
||||||
case reparseTagSymlink: |
|
||||||
default: |
|
||||||
return nil, &UnsupportedReparsePointError{tag} |
|
||||||
} |
|
||||||
nameOffset := 16 + binary.LittleEndian.Uint16(b[12:14]) |
|
||||||
if !isMountPoint { |
|
||||||
nameOffset += 4 |
|
||||||
} |
|
||||||
nameLength := binary.LittleEndian.Uint16(b[14:16]) |
|
||||||
name := make([]uint16, nameLength/2) |
|
||||||
err := binary.Read(bytes.NewReader(b[nameOffset:nameOffset+nameLength]), binary.LittleEndian, &name) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
return &ReparsePoint{string(utf16.Decode(name)), isMountPoint}, nil |
|
||||||
} |
|
||||||
|
|
||||||
func isDriveLetter(c byte) bool { |
|
||||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') |
|
||||||
} |
|
||||||
|
|
||||||
// EncodeReparsePoint encodes a Win32 REPARSE_DATA_BUFFER structure describing a symlink or
|
|
||||||
// mount point.
|
|
||||||
func EncodeReparsePoint(rp *ReparsePoint) []byte { |
|
||||||
// Generate an NT path and determine if this is a relative path.
|
|
||||||
var ntTarget string |
|
||||||
relative := false |
|
||||||
if strings.HasPrefix(rp.Target, `\\?\`) { |
|
||||||
ntTarget = rp.Target |
|
||||||
} else if strings.HasPrefix(rp.Target, `\\`) { |
|
||||||
ntTarget = `\??\UNC\` + rp.Target[2:] |
|
||||||
} else if len(rp.Target) >= 2 && isDriveLetter(rp.Target[0]) && rp.Target[1] == ':' { |
|
||||||
ntTarget = `\??\` + rp.Target |
|
||||||
} else { |
|
||||||
ntTarget = rp.Target |
|
||||||
relative = true |
|
||||||
} |
|
||||||
|
|
||||||
// The paths must be NUL-terminated even though they are counted strings.
|
|
||||||
target16 := utf16.Encode([]rune(rp.Target + "\x00")) |
|
||||||
ntTarget16 := utf16.Encode([]rune(ntTarget + "\x00")) |
|
||||||
|
|
||||||
size := int(unsafe.Sizeof(reparseDataBuffer{})) - 8 |
|
||||||
size += len(ntTarget16)*2 + len(target16)*2 |
|
||||||
|
|
||||||
tag := uint32(reparseTagMountPoint) |
|
||||||
if !rp.IsMountPoint { |
|
||||||
tag = reparseTagSymlink |
|
||||||
size += 4 // Add room for symlink flags
|
|
||||||
} |
|
||||||
|
|
||||||
data := reparseDataBuffer{ |
|
||||||
ReparseTag: tag, |
|
||||||
ReparseDataLength: uint16(size), |
|
||||||
SubstituteNameOffset: 0, |
|
||||||
SubstituteNameLength: uint16((len(ntTarget16) - 1) * 2), |
|
||||||
PrintNameOffset: uint16(len(ntTarget16) * 2), |
|
||||||
PrintNameLength: uint16((len(target16) - 1) * 2), |
|
||||||
} |
|
||||||
|
|
||||||
var b bytes.Buffer |
|
||||||
binary.Write(&b, binary.LittleEndian, &data) |
|
||||||
if !rp.IsMountPoint { |
|
||||||
flags := uint32(0) |
|
||||||
if relative { |
|
||||||
flags |= 1 |
|
||||||
} |
|
||||||
binary.Write(&b, binary.LittleEndian, flags) |
|
||||||
} |
|
||||||
|
|
||||||
binary.Write(&b, binary.LittleEndian, ntTarget16) |
|
||||||
binary.Write(&b, binary.LittleEndian, target16) |
|
||||||
return b.Bytes() |
|
||||||
} |
|
@ -1,96 +0,0 @@ |
|||||||
package winio |
|
||||||
|
|
||||||
import ( |
|
||||||
"syscall" |
|
||||||
"unsafe" |
|
||||||
) |
|
||||||
|
|
||||||
//sys lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountNameW
|
|
||||||
//sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW
|
|
||||||
//sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW
|
|
||||||
//sys convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) = advapi32.ConvertSecurityDescriptorToStringSecurityDescriptorW
|
|
||||||
//sys localFree(mem uintptr) = LocalFree
|
|
||||||
//sys getSecurityDescriptorLength(sd uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength
|
|
||||||
|
|
||||||
const ( |
|
||||||
cERROR_NONE_MAPPED = syscall.Errno(1332) |
|
||||||
) |
|
||||||
|
|
||||||
type AccountLookupError struct { |
|
||||||
Name string |
|
||||||
Err error |
|
||||||
} |
|
||||||
|
|
||||||
func (e *AccountLookupError) Error() string { |
|
||||||
if e.Name == "" { |
|
||||||
return "lookup account: empty account name specified" |
|
||||||
} |
|
||||||
var s string |
|
||||||
switch e.Err { |
|
||||||
case cERROR_NONE_MAPPED: |
|
||||||
s = "not found" |
|
||||||
default: |
|
||||||
s = e.Err.Error() |
|
||||||
} |
|
||||||
return "lookup account " + e.Name + ": " + s |
|
||||||
} |
|
||||||
|
|
||||||
type SddlConversionError struct { |
|
||||||
Sddl string |
|
||||||
Err error |
|
||||||
} |
|
||||||
|
|
||||||
func (e *SddlConversionError) Error() string { |
|
||||||
return "convert " + e.Sddl + ": " + e.Err.Error() |
|
||||||
} |
|
||||||
|
|
||||||
// LookupSidByName looks up the SID of an account by name
|
|
||||||
func LookupSidByName(name string) (sid string, err error) { |
|
||||||
if name == "" { |
|
||||||
return "", &AccountLookupError{name, cERROR_NONE_MAPPED} |
|
||||||
} |
|
||||||
|
|
||||||
var sidSize, sidNameUse, refDomainSize uint32 |
|
||||||
err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse) |
|
||||||
if err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER { |
|
||||||
return "", &AccountLookupError{name, err} |
|
||||||
} |
|
||||||
sidBuffer := make([]byte, sidSize) |
|
||||||
refDomainBuffer := make([]uint16, refDomainSize) |
|
||||||
err = lookupAccountName(nil, name, &sidBuffer[0], &sidSize, &refDomainBuffer[0], &refDomainSize, &sidNameUse) |
|
||||||
if err != nil { |
|
||||||
return "", &AccountLookupError{name, err} |
|
||||||
} |
|
||||||
var strBuffer *uint16 |
|
||||||
err = convertSidToStringSid(&sidBuffer[0], &strBuffer) |
|
||||||
if err != nil { |
|
||||||
return "", &AccountLookupError{name, err} |
|
||||||
} |
|
||||||
sid = syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:]) |
|
||||||
localFree(uintptr(unsafe.Pointer(strBuffer))) |
|
||||||
return sid, nil |
|
||||||
} |
|
||||||
|
|
||||||
func SddlToSecurityDescriptor(sddl string) ([]byte, error) { |
|
||||||
var sdBuffer uintptr |
|
||||||
err := convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &sdBuffer, nil) |
|
||||||
if err != nil { |
|
||||||
return nil, &SddlConversionError{sddl, err} |
|
||||||
} |
|
||||||
defer localFree(sdBuffer) |
|
||||||
sd := make([]byte, getSecurityDescriptorLength(sdBuffer)) |
|
||||||
copy(sd, (*[0xffff]byte)(unsafe.Pointer(sdBuffer))[:len(sd)]) |
|
||||||
return sd, nil |
|
||||||
} |
|
||||||
|
|
||||||
func SecurityDescriptorToSddl(sd []byte) (string, error) { |
|
||||||
var sddl *uint16 |
|
||||||
// The returned string length seems to including an aribtrary number of terminating NULs.
|
|
||||||
// Don't use it.
|
|
||||||
err := convertSecurityDescriptorToStringSecurityDescriptor(&sd[0], 1, 0xff, &sddl, nil) |
|
||||||
if err != nil { |
|
||||||
return "", err |
|
||||||
} |
|
||||||
defer localFree(uintptr(unsafe.Pointer(sddl))) |
|
||||||
return syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(sddl))[:]), nil |
|
||||||
} |
|
@ -1,3 +0,0 @@ |
|||||||
package winio |
|
||||||
|
|
||||||
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall.go file.go pipe.go sd.go fileinfo.go privilege.go backup.go
|
|
@ -1,492 +0,0 @@ |
|||||||
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
|
|
||||||
|
|
||||||
package winio |
|
||||||
|
|
||||||
import "unsafe" |
|
||||||
import "syscall" |
|
||||||
|
|
||||||
var _ unsafe.Pointer |
|
||||||
|
|
||||||
var ( |
|
||||||
modkernel32 = syscall.NewLazyDLL("kernel32.dll") |
|
||||||
modwinmm = syscall.NewLazyDLL("winmm.dll") |
|
||||||
modadvapi32 = syscall.NewLazyDLL("advapi32.dll") |
|
||||||
|
|
||||||
procCancelIoEx = modkernel32.NewProc("CancelIoEx") |
|
||||||
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort") |
|
||||||
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus") |
|
||||||
procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes") |
|
||||||
proctimeBeginPeriod = modwinmm.NewProc("timeBeginPeriod") |
|
||||||
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe") |
|
||||||
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW") |
|
||||||
procCreateFileW = modkernel32.NewProc("CreateFileW") |
|
||||||
procWaitNamedPipeW = modkernel32.NewProc("WaitNamedPipeW") |
|
||||||
procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo") |
|
||||||
procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW") |
|
||||||
procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW") |
|
||||||
procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW") |
|
||||||
procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW") |
|
||||||
procConvertSecurityDescriptorToStringSecurityDescriptorW = modadvapi32.NewProc("ConvertSecurityDescriptorToStringSecurityDescriptorW") |
|
||||||
procLocalFree = modkernel32.NewProc("LocalFree") |
|
||||||
procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength") |
|
||||||
procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx") |
|
||||||
procSetFileInformationByHandle = modkernel32.NewProc("SetFileInformationByHandle") |
|
||||||
procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges") |
|
||||||
procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf") |
|
||||||
procRevertToSelf = modadvapi32.NewProc("RevertToSelf") |
|
||||||
procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken") |
|
||||||
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread") |
|
||||||
procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW") |
|
||||||
procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW") |
|
||||||
procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW") |
|
||||||
procBackupRead = modkernel32.NewProc("BackupRead") |
|
||||||
procBackupWrite = modkernel32.NewProc("BackupWrite") |
|
||||||
) |
|
||||||
|
|
||||||
func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) { |
|
||||||
r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(file), uintptr(unsafe.Pointer(o)), 0) |
|
||||||
if r1 == 0 { |
|
||||||
if e1 != 0 { |
|
||||||
err = error(e1) |
|
||||||
} else { |
|
||||||
err = syscall.EINVAL |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) { |
|
||||||
r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0) |
|
||||||
newport = syscall.Handle(r0) |
|
||||||
if newport == 0 { |
|
||||||
if e1 != 0 { |
|
||||||
err = error(e1) |
|
||||||
} else { |
|
||||||
err = syscall.EINVAL |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) { |
|
||||||
r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout), 0) |
|
||||||
if r1 == 0 { |
|
||||||
if e1 != 0 { |
|
||||||
err = error(e1) |
|
||||||
} else { |
|
||||||
err = syscall.EINVAL |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) { |
|
||||||
r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(h), uintptr(flags), 0) |
|
||||||
if r1 == 0 { |
|
||||||
if e1 != 0 { |
|
||||||
err = error(e1) |
|
||||||
} else { |
|
||||||
err = syscall.EINVAL |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func timeBeginPeriod(period uint32) (n int32) { |
|
||||||
r0, _, _ := syscall.Syscall(proctimeBeginPeriod.Addr(), 1, uintptr(period), 0, 0) |
|
||||||
n = int32(r0) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) { |
|
||||||
r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(o)), 0) |
|
||||||
if r1 == 0 { |
|
||||||
if e1 != 0 { |
|
||||||
err = error(e1) |
|
||||||
} else { |
|
||||||
err = syscall.EINVAL |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *securityAttributes) (handle syscall.Handle, err error) { |
|
||||||
var _p0 *uint16 |
|
||||||
_p0, err = syscall.UTF16PtrFromString(name) |
|
||||||
if err != nil { |
|
||||||
return |
|
||||||
} |
|
||||||
return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa) |
|
||||||
} |
|
||||||
|
|
||||||
func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *securityAttributes) (handle syscall.Handle, err error) { |
|
||||||
r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0) |
|
||||||
handle = syscall.Handle(r0) |
|
||||||
if handle == syscall.InvalidHandle { |
|
||||||
if e1 != 0 { |
|
||||||
err = error(e1) |
|
||||||
} else { |
|
||||||
err = syscall.EINVAL |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func createFile(name string, access uint32, mode uint32, sa *securityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) { |
|
||||||
var _p0 *uint16 |
|
||||||
_p0, err = syscall.UTF16PtrFromString(name) |
|
||||||
if err != nil { |
|
||||||
return |
|
||||||
} |
|
||||||
return _createFile(_p0, access, mode, sa, createmode, attrs, templatefile) |
|
||||||
} |
|
||||||
|
|
||||||
func _createFile(name *uint16, access uint32, mode uint32, sa *securityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) { |
|
||||||
r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0) |
|
||||||
handle = syscall.Handle(r0) |
|
||||||
if handle == syscall.InvalidHandle { |
|
||||||
if e1 != 0 { |
|
||||||
err = error(e1) |
|
||||||
} else { |
|
||||||
err = syscall.EINVAL |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func waitNamedPipe(name string, timeout uint32) (err error) { |
|
||||||
var _p0 *uint16 |
|
||||||
_p0, err = syscall.UTF16PtrFromString(name) |
|
||||||
if err != nil { |
|
||||||
return |
|
||||||
} |
|
||||||
return _waitNamedPipe(_p0, timeout) |
|
||||||
} |
|
||||||
|
|
||||||
func _waitNamedPipe(name *uint16, timeout uint32) (err error) { |
|
||||||
r1, _, e1 := syscall.Syscall(procWaitNamedPipeW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(timeout), 0) |
|
||||||
if r1 == 0 { |
|
||||||
if e1 != 0 { |
|
||||||
err = error(e1) |
|
||||||
} else { |
|
||||||
err = syscall.EINVAL |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) { |
|
||||||
r1, _, e1 := syscall.Syscall6(procGetNamedPipeInfo.Addr(), 5, uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)), 0) |
|
||||||
if r1 == 0 { |
|
||||||
if e1 != 0 { |
|
||||||
err = error(e1) |
|
||||||
} else { |
|
||||||
err = syscall.EINVAL |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) { |
|
||||||
r1, _, e1 := syscall.Syscall9(procGetNamedPipeHandleStateW.Addr(), 7, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize), 0, 0) |
|
||||||
if r1 == 0 { |
|
||||||
if e1 != 0 { |
|
||||||
err = error(e1) |
|
||||||
} else { |
|
||||||
err = syscall.EINVAL |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) { |
|
||||||
var _p0 *uint16 |
|
||||||
_p0, err = syscall.UTF16PtrFromString(accountName) |
|
||||||
if err != nil { |
|
||||||
return |
|
||||||
} |
|
||||||
return _lookupAccountName(systemName, _p0, sid, sidSize, refDomain, refDomainSize, sidNameUse) |
|
||||||
} |
|
||||||
|
|
||||||
func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) { |
|
||||||
r1, _, e1 := syscall.Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0) |
|
||||||
if r1 == 0 { |
|
||||||
if e1 != 0 { |
|
||||||
err = error(e1) |
|
||||||
} else { |
|
||||||
err = syscall.EINVAL |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func convertSidToStringSid(sid *byte, str **uint16) (err error) { |
|
||||||
r1, _, e1 := syscall.Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)), 0) |
|
||||||
if r1 == 0 { |
|
||||||
if e1 != 0 { |
|
||||||
err = error(e1) |
|
||||||
} else { |
|
||||||
err = syscall.EINVAL |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) { |
|
||||||
var _p0 *uint16 |
|
||||||
_p0, err = syscall.UTF16PtrFromString(str) |
|
||||||
if err != nil { |
|
||||||
return |
|
||||||
} |
|
||||||
return _convertStringSecurityDescriptorToSecurityDescriptor(_p0, revision, sd, size) |
|
||||||
} |
|
||||||
|
|
||||||
func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision uint32, sd *uintptr, size *uint32) (err error) { |
|
||||||
r1, _, e1 := syscall.Syscall6(procConvertStringSecurityDescriptorToSecurityDescriptorW.Addr(), 4, uintptr(unsafe.Pointer(str)), uintptr(revision), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(size)), 0, 0) |
|
||||||
if r1 == 0 { |
|
||||||
if e1 != 0 { |
|
||||||
err = error(e1) |
|
||||||
} else { |
|
||||||
err = syscall.EINVAL |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) { |
|
||||||
r1, _, e1 := syscall.Syscall6(procConvertSecurityDescriptorToStringSecurityDescriptorW.Addr(), 5, uintptr(unsafe.Pointer(sd)), uintptr(revision), uintptr(secInfo), uintptr(unsafe.Pointer(sddl)), uintptr(unsafe.Pointer(sddlSize)), 0) |
|
||||||
if r1 == 0 { |
|
||||||
if e1 != 0 { |
|
||||||
err = error(e1) |
|
||||||
} else { |
|
||||||
err = syscall.EINVAL |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func localFree(mem uintptr) { |
|
||||||
syscall.Syscall(procLocalFree.Addr(), 1, uintptr(mem), 0, 0) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func getSecurityDescriptorLength(sd uintptr) (len uint32) { |
|
||||||
r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0) |
|
||||||
len = uint32(r0) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) { |
|
||||||
r1, _, e1 := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0) |
|
||||||
if r1 == 0 { |
|
||||||
if e1 != 0 { |
|
||||||
err = error(e1) |
|
||||||
} else { |
|
||||||
err = syscall.EINVAL |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) { |
|
||||||
r1, _, e1 := syscall.Syscall6(procSetFileInformationByHandle.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0) |
|
||||||
if r1 == 0 { |
|
||||||
if e1 != 0 { |
|
||||||
err = error(e1) |
|
||||||
} else { |
|
||||||
err = syscall.EINVAL |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func adjustTokenPrivileges(token syscall.Handle, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) { |
|
||||||
var _p0 uint32 |
|
||||||
if releaseAll { |
|
||||||
_p0 = 1 |
|
||||||
} else { |
|
||||||
_p0 = 0 |
|
||||||
} |
|
||||||
r0, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize))) |
|
||||||
success = r0 != 0 |
|
||||||
if true { |
|
||||||
if e1 != 0 { |
|
||||||
err = error(e1) |
|
||||||
} else { |
|
||||||
err = syscall.EINVAL |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func impersonateSelf(level uint32) (err error) { |
|
||||||
r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(level), 0, 0) |
|
||||||
if r1 == 0 { |
|
||||||
if e1 != 0 { |
|
||||||
err = error(e1) |
|
||||||
} else { |
|
||||||
err = syscall.EINVAL |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func revertToSelf() (err error) { |
|
||||||
r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0) |
|
||||||
if r1 == 0 { |
|
||||||
if e1 != 0 { |
|
||||||
err = error(e1) |
|
||||||
} else { |
|
||||||
err = syscall.EINVAL |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *syscall.Handle) (err error) { |
|
||||||
var _p0 uint32 |
|
||||||
if openAsSelf { |
|
||||||
_p0 = 1 |
|
||||||
} else { |
|
||||||
_p0 = 0 |
|
||||||
} |
|
||||||
r1, _, e1 := syscall.Syscall6(procOpenThreadToken.Addr(), 4, uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0) |
|
||||||
if r1 == 0 { |
|
||||||
if e1 != 0 { |
|
||||||
err = error(e1) |
|
||||||
} else { |
|
||||||
err = syscall.EINVAL |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func getCurrentThread() (h syscall.Handle) { |
|
||||||
r0, _, _ := syscall.Syscall(procGetCurrentThread.Addr(), 0, 0, 0, 0) |
|
||||||
h = syscall.Handle(r0) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) { |
|
||||||
var _p0 *uint16 |
|
||||||
_p0, err = syscall.UTF16PtrFromString(systemName) |
|
||||||
if err != nil { |
|
||||||
return |
|
||||||
} |
|
||||||
var _p1 *uint16 |
|
||||||
_p1, err = syscall.UTF16PtrFromString(name) |
|
||||||
if err != nil { |
|
||||||
return |
|
||||||
} |
|
||||||
return _lookupPrivilegeValue(_p0, _p1, luid) |
|
||||||
} |
|
||||||
|
|
||||||
func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err error) { |
|
||||||
r1, _, e1 := syscall.Syscall(procLookupPrivilegeValueW.Addr(), 3, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid))) |
|
||||||
if r1 == 0 { |
|
||||||
if e1 != 0 { |
|
||||||
err = error(e1) |
|
||||||
} else { |
|
||||||
err = syscall.EINVAL |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) { |
|
||||||
var _p0 *uint16 |
|
||||||
_p0, err = syscall.UTF16PtrFromString(systemName) |
|
||||||
if err != nil { |
|
||||||
return |
|
||||||
} |
|
||||||
return _lookupPrivilegeName(_p0, luid, buffer, size) |
|
||||||
} |
|
||||||
|
|
||||||
func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) { |
|
||||||
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeNameW.Addr(), 4, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), 0, 0) |
|
||||||
if r1 == 0 { |
|
||||||
if e1 != 0 { |
|
||||||
err = error(e1) |
|
||||||
} else { |
|
||||||
err = syscall.EINVAL |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) { |
|
||||||
var _p0 *uint16 |
|
||||||
_p0, err = syscall.UTF16PtrFromString(systemName) |
|
||||||
if err != nil { |
|
||||||
return |
|
||||||
} |
|
||||||
return _lookupPrivilegeDisplayName(_p0, name, buffer, size, languageId) |
|
||||||
} |
|
||||||
|
|
||||||
func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) { |
|
||||||
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeDisplayNameW.Addr(), 5, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)), 0) |
|
||||||
if r1 == 0 { |
|
||||||
if e1 != 0 { |
|
||||||
err = error(e1) |
|
||||||
} else { |
|
||||||
err = syscall.EINVAL |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) { |
|
||||||
var _p0 *byte |
|
||||||
if len(b) > 0 { |
|
||||||
_p0 = &b[0] |
|
||||||
} |
|
||||||
var _p1 uint32 |
|
||||||
if abort { |
|
||||||
_p1 = 1 |
|
||||||
} else { |
|
||||||
_p1 = 0 |
|
||||||
} |
|
||||||
var _p2 uint32 |
|
||||||
if processSecurity { |
|
||||||
_p2 = 1 |
|
||||||
} else { |
|
||||||
_p2 = 0 |
|
||||||
} |
|
||||||
r1, _, e1 := syscall.Syscall9(procBackupRead.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0) |
|
||||||
if r1 == 0 { |
|
||||||
if e1 != 0 { |
|
||||||
err = error(e1) |
|
||||||
} else { |
|
||||||
err = syscall.EINVAL |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) { |
|
||||||
var _p0 *byte |
|
||||||
if len(b) > 0 { |
|
||||||
_p0 = &b[0] |
|
||||||
} |
|
||||||
var _p1 uint32 |
|
||||||
if abort { |
|
||||||
_p1 = 1 |
|
||||||
} else { |
|
||||||
_p1 = 0 |
|
||||||
} |
|
||||||
var _p2 uint32 |
|
||||||
if processSecurity { |
|
||||||
_p2 = 1 |
|
||||||
} else { |
|
||||||
_p2 = 0 |
|
||||||
} |
|
||||||
r1, _, e1 := syscall.Syscall9(procBackupWrite.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0) |
|
||||||
if r1 == 0 { |
|
||||||
if e1 != 0 { |
|
||||||
err = error(e1) |
|
||||||
} else { |
|
||||||
err = syscall.EINVAL |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
@ -1,19 +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 go1.5
|
|
||||||
|
|
||||||
package ctxhttp |
|
||||||
|
|
||||||
import "net/http" |
|
||||||
|
|
||||||
func canceler(client *http.Client, req *http.Request) func() { |
|
||||||
// TODO(djd): Respect any existing value of req.Cancel.
|
|
||||||
ch := make(chan struct{}) |
|
||||||
req.Cancel = ch |
|
||||||
|
|
||||||
return func() { |
|
||||||
close(ch) |
|
||||||
} |
|
||||||
} |
|
@ -1,23 +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 !go1.5
|
|
||||||
|
|
||||||
package ctxhttp |
|
||||||
|
|
||||||
import "net/http" |
|
||||||
|
|
||||||
type requestCanceler interface { |
|
||||||
CancelRequest(*http.Request) |
|
||||||
} |
|
||||||
|
|
||||||
func canceler(client *http.Client, req *http.Request) func() { |
|
||||||
rc, ok := client.Transport.(requestCanceler) |
|
||||||
if !ok { |
|
||||||
return func() {} |
|
||||||
} |
|
||||||
return func() { |
|
||||||
rc.CancelRequest(req) |
|
||||||
} |
|
||||||
} |
|
@ -1,140 +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.
|
|
||||||
|
|
||||||
// Package ctxhttp provides helper functions for performing context-aware HTTP requests.
|
|
||||||
package ctxhttp |
|
||||||
|
|
||||||
import ( |
|
||||||
"io" |
|
||||||
"net/http" |
|
||||||
"net/url" |
|
||||||
"strings" |
|
||||||
|
|
||||||
"golang.org/x/net/context" |
|
||||||
) |
|
||||||
|
|
||||||
func nop() {} |
|
||||||
|
|
||||||
var ( |
|
||||||
testHookContextDoneBeforeHeaders = nop |
|
||||||
testHookDoReturned = nop |
|
||||||
testHookDidBodyClose = nop |
|
||||||
) |
|
||||||
|
|
||||||
// Do sends an HTTP request with the provided http.Client and returns an HTTP response.
|
|
||||||
// If the client is nil, http.DefaultClient is used.
|
|
||||||
// If the context is canceled or times out, ctx.Err() will be returned.
|
|
||||||
func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) { |
|
||||||
if client == nil { |
|
||||||
client = http.DefaultClient |
|
||||||
} |
|
||||||
|
|
||||||
// Request cancelation changed in Go 1.5, see cancelreq.go and cancelreq_go14.go.
|
|
||||||
cancel := canceler(client, req) |
|
||||||
|
|
||||||
type responseAndError struct { |
|
||||||
resp *http.Response |
|
||||||
err error |
|
||||||
} |
|
||||||
result := make(chan responseAndError, 1) |
|
||||||
|
|
||||||
go func() { |
|
||||||
resp, err := client.Do(req) |
|
||||||
testHookDoReturned() |
|
||||||
result <- responseAndError{resp, err} |
|
||||||
}() |
|
||||||
|
|
||||||
var resp *http.Response |
|
||||||
|
|
||||||
select { |
|
||||||
case <-ctx.Done(): |
|
||||||
testHookContextDoneBeforeHeaders() |
|
||||||
cancel() |
|
||||||
// Clean up after the goroutine calling client.Do:
|
|
||||||
go func() { |
|
||||||
if r := <-result; r.resp != nil { |
|
||||||
testHookDidBodyClose() |
|
||||||
r.resp.Body.Close() |
|
||||||
} |
|
||||||
}() |
|
||||||
return nil, ctx.Err() |
|
||||||
case r := <-result: |
|
||||||
var err error |
|
||||||
resp, err = r.resp, r.err |
|
||||||
if err != nil { |
|
||||||
return resp, err |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
c := make(chan struct{}) |
|
||||||
go func() { |
|
||||||
select { |
|
||||||
case <-ctx.Done(): |
|
||||||
cancel() |
|
||||||
case <-c: |
|
||||||
// The response's Body is closed.
|
|
||||||
} |
|
||||||
}() |
|
||||||
resp.Body = ¬ifyingReader{resp.Body, c} |
|
||||||
|
|
||||||
return resp, nil |
|
||||||
} |
|
||||||
|
|
||||||
// Get issues a GET request via the Do function.
|
|
||||||
func Get(ctx context.Context, client *http.Client, url string) (*http.Response, error) { |
|
||||||
req, err := http.NewRequest("GET", url, nil) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
return Do(ctx, client, req) |
|
||||||
} |
|
||||||
|
|
||||||
// Head issues a HEAD request via the Do function.
|
|
||||||
func Head(ctx context.Context, client *http.Client, url string) (*http.Response, error) { |
|
||||||
req, err := http.NewRequest("HEAD", url, nil) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
return Do(ctx, client, req) |
|
||||||
} |
|
||||||
|
|
||||||
// Post issues a POST request via the Do function.
|
|
||||||
func Post(ctx context.Context, client *http.Client, url string, bodyType string, body io.Reader) (*http.Response, error) { |
|
||||||
req, err := http.NewRequest("POST", url, body) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
req.Header.Set("Content-Type", bodyType) |
|
||||||
return Do(ctx, client, req) |
|
||||||
} |
|
||||||
|
|
||||||
// PostForm issues a POST request via the Do function.
|
|
||||||
func PostForm(ctx context.Context, client *http.Client, url string, data url.Values) (*http.Response, error) { |
|
||||||
return Post(ctx, client, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) |
|
||||||
} |
|
||||||
|
|
||||||
// notifyingReader is an io.ReadCloser that closes the notify channel after
|
|
||||||
// Close is called or a Read fails on the underlying ReadCloser.
|
|
||||||
type notifyingReader struct { |
|
||||||
io.ReadCloser |
|
||||||
notify chan<- struct{} |
|
||||||
} |
|
||||||
|
|
||||||
func (r *notifyingReader) Read(p []byte) (int, error) { |
|
||||||
n, err := r.ReadCloser.Read(p) |
|
||||||
if err != nil && r.notify != nil { |
|
||||||
close(r.notify) |
|
||||||
r.notify = nil |
|
||||||
} |
|
||||||
return n, err |
|
||||||
} |
|
||||||
|
|
||||||
func (r *notifyingReader) Close() error { |
|
||||||
err := r.ReadCloser.Close() |
|
||||||
if r.notify != nil { |
|
||||||
close(r.notify) |
|
||||||
r.notify = nil |
|
||||||
} |
|
||||||
return err |
|
||||||
} |
|
@ -0,0 +1,72 @@ |
|||||||
|
// Copyright 2016 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 go1.7
|
||||||
|
|
||||||
|
package context |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" // standard library's context, as of Go 1.7
|
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
todo = context.TODO() |
||||||
|
background = context.Background() |
||||||
|
) |
||||||
|
|
||||||
|
// Canceled is the error returned by Context.Err when the context is canceled.
|
||||||
|
var Canceled = context.Canceled |
||||||
|
|
||||||
|
// DeadlineExceeded is the error returned by Context.Err when the context's
|
||||||
|
// deadline passes.
|
||||||
|
var DeadlineExceeded = context.DeadlineExceeded |
||||||
|
|
||||||
|
// WithCancel returns a copy of parent with a new Done channel. The returned
|
||||||
|
// context's Done channel is closed when the returned cancel function is called
|
||||||
|
// or when the parent context's Done channel is closed, whichever happens first.
|
||||||
|
//
|
||||||
|
// Canceling this context releases resources associated with it, so code should
|
||||||
|
// call cancel as soon as the operations running in this Context complete.
|
||||||
|
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { |
||||||
|
ctx, f := context.WithCancel(parent) |
||||||
|
return ctx, CancelFunc(f) |
||||||
|
} |
||||||
|
|
||||||
|
// WithDeadline returns a copy of the parent context with the deadline adjusted
|
||||||
|
// to be no later than d. If the parent's deadline is already earlier than d,
|
||||||
|
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
|
||||||
|
// context's Done channel is closed when the deadline expires, when the returned
|
||||||
|
// cancel function is called, or when the parent context's Done channel is
|
||||||
|
// closed, whichever happens first.
|
||||||
|
//
|
||||||
|
// Canceling this context releases resources associated with it, so code should
|
||||||
|
// call cancel as soon as the operations running in this Context complete.
|
||||||
|
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { |
||||||
|
ctx, f := context.WithDeadline(parent, deadline) |
||||||
|
return ctx, CancelFunc(f) |
||||||
|
} |
||||||
|
|
||||||
|
// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
|
||||||
|
//
|
||||||
|
// Canceling this context releases resources associated with it, so code should
|
||||||
|
// call cancel as soon as the operations running in this Context complete:
|
||||||
|
//
|
||||||
|
// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
|
||||||
|
// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
|
||||||
|
// defer cancel() // releases resources if slowOperation completes before timeout elapses
|
||||||
|
// return slowOperation(ctx)
|
||||||
|
// }
|
||||||
|
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { |
||||||
|
return WithDeadline(parent, time.Now().Add(timeout)) |
||||||
|
} |
||||||
|
|
||||||
|
// WithValue returns a copy of parent in which the value associated with key is
|
||||||
|
// val.
|
||||||
|
//
|
||||||
|
// Use context Values only for request-scoped data that transits processes and
|
||||||
|
// APIs, not for passing optional parameters to functions.
|
||||||
|
func WithValue(parent Context, key interface{}, val interface{}) Context { |
||||||
|
return context.WithValue(parent, key, val) |
||||||
|
} |
@ -0,0 +1,300 @@ |
|||||||
|
// 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 !go1.7
|
||||||
|
|
||||||
|
package context |
||||||
|
|
||||||
|
import ( |
||||||
|
"errors" |
||||||
|
"fmt" |
||||||
|
"sync" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
// An emptyCtx is never canceled, has no values, and has no deadline. It is not
|
||||||
|
// struct{}, since vars of this type must have distinct addresses.
|
||||||
|
type emptyCtx int |
||||||
|
|
||||||
|
func (*emptyCtx) Deadline() (deadline time.Time, ok bool) { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func (*emptyCtx) Done() <-chan struct{} { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (*emptyCtx) Err() error { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (*emptyCtx) Value(key interface{}) interface{} { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (e *emptyCtx) String() string { |
||||||
|
switch e { |
||||||
|
case background: |
||||||
|
return "context.Background" |
||||||
|
case todo: |
||||||
|
return "context.TODO" |
||||||
|
} |
||||||
|
return "unknown empty Context" |
||||||
|
} |
||||||
|
|
||||||
|
var ( |
||||||
|
background = new(emptyCtx) |
||||||
|
todo = new(emptyCtx) |
||||||
|
) |
||||||
|
|
||||||
|
// Canceled is the error returned by Context.Err when the context is canceled.
|
||||||
|
var Canceled = errors.New("context canceled") |
||||||
|
|
||||||
|
// DeadlineExceeded is the error returned by Context.Err when the context's
|
||||||
|
// deadline passes.
|
||||||
|
var DeadlineExceeded = errors.New("context deadline exceeded") |
||||||
|
|
||||||
|
// WithCancel returns a copy of parent with a new Done channel. The returned
|
||||||
|
// context's Done channel is closed when the returned cancel function is called
|
||||||
|
// or when the parent context's Done channel is closed, whichever happens first.
|
||||||
|
//
|
||||||
|
// Canceling this context releases resources associated with it, so code should
|
||||||
|
// call cancel as soon as the operations running in this Context complete.
|
||||||
|
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { |
||||||
|
c := newCancelCtx(parent) |
||||||
|
propagateCancel(parent, c) |
||||||
|
return c, func() { c.cancel(true, Canceled) } |
||||||
|
} |
||||||
|
|
||||||
|
// newCancelCtx returns an initialized cancelCtx.
|
||||||
|
func newCancelCtx(parent Context) *cancelCtx { |
||||||
|
return &cancelCtx{ |
||||||
|
Context: parent, |
||||||
|
done: make(chan struct{}), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// propagateCancel arranges for child to be canceled when parent is.
|
||||||
|
func propagateCancel(parent Context, child canceler) { |
||||||
|
if parent.Done() == nil { |
||||||
|
return // parent is never canceled
|
||||||
|
} |
||||||
|
if p, ok := parentCancelCtx(parent); ok { |
||||||
|
p.mu.Lock() |
||||||
|
if p.err != nil { |
||||||
|
// parent has already been canceled
|
||||||
|
child.cancel(false, p.err) |
||||||
|
} else { |
||||||
|
if p.children == nil { |
||||||
|
p.children = make(map[canceler]bool) |
||||||
|
} |
||||||
|
p.children[child] = true |
||||||
|
} |
||||||
|
p.mu.Unlock() |
||||||
|
} else { |
||||||
|
go func() { |
||||||
|
select { |
||||||
|
case <-parent.Done(): |
||||||
|
child.cancel(false, parent.Err()) |
||||||
|
case <-child.Done(): |
||||||
|
} |
||||||
|
}() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// parentCancelCtx follows a chain of parent references until it finds a
|
||||||
|
// *cancelCtx. This function understands how each of the concrete types in this
|
||||||
|
// package represents its parent.
|
||||||
|
func parentCancelCtx(parent Context) (*cancelCtx, bool) { |
||||||
|
for { |
||||||
|
switch c := parent.(type) { |
||||||
|
case *cancelCtx: |
||||||
|
return c, true |
||||||
|
case *timerCtx: |
||||||
|
return c.cancelCtx, true |
||||||
|
case *valueCtx: |
||||||
|
parent = c.Context |
||||||
|
default: |
||||||
|
return nil, false |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// removeChild removes a context from its parent.
|
||||||
|
func removeChild(parent Context, child canceler) { |
||||||
|
p, ok := parentCancelCtx(parent) |
||||||
|
if !ok { |
||||||
|
return |
||||||
|
} |
||||||
|
p.mu.Lock() |
||||||
|
if p.children != nil { |
||||||
|
delete(p.children, child) |
||||||
|
} |
||||||
|
p.mu.Unlock() |
||||||
|
} |
||||||
|
|
||||||
|
// A canceler is a context type that can be canceled directly. The
|
||||||
|
// implementations are *cancelCtx and *timerCtx.
|
||||||
|
type canceler interface { |
||||||
|
cancel(removeFromParent bool, err error) |
||||||
|
Done() <-chan struct{} |
||||||
|
} |
||||||
|
|
||||||
|
// A cancelCtx can be canceled. When canceled, it also cancels any children
|
||||||
|
// that implement canceler.
|
||||||
|
type cancelCtx struct { |
||||||
|
Context |
||||||
|
|
||||||
|
done chan struct{} // closed by the first cancel call.
|
||||||
|
|
||||||
|
mu sync.Mutex |
||||||
|
children map[canceler]bool // set to nil by the first cancel call
|
||||||
|
err error // set to non-nil by the first cancel call
|
||||||
|
} |
||||||
|
|
||||||
|
func (c *cancelCtx) Done() <-chan struct{} { |
||||||
|
return c.done |
||||||
|
} |
||||||
|
|
||||||
|
func (c *cancelCtx) Err() error { |
||||||
|
c.mu.Lock() |
||||||
|
defer c.mu.Unlock() |
||||||
|
return c.err |
||||||
|
} |
||||||
|
|
||||||
|
func (c *cancelCtx) String() string { |
||||||
|
return fmt.Sprintf("%v.WithCancel", c.Context) |
||||||
|
} |
||||||
|
|
||||||
|
// cancel closes c.done, cancels each of c's children, and, if
|
||||||
|
// removeFromParent is true, removes c from its parent's children.
|
||||||
|
func (c *cancelCtx) cancel(removeFromParent bool, err error) { |
||||||
|
if err == nil { |
||||||
|
panic("context: internal error: missing cancel error") |
||||||
|
} |
||||||
|
c.mu.Lock() |
||||||
|
if c.err != nil { |
||||||
|
c.mu.Unlock() |
||||||
|
return // already canceled
|
||||||
|
} |
||||||
|
c.err = err |
||||||
|
close(c.done) |
||||||
|
for child := range c.children { |
||||||
|
// NOTE: acquiring the child's lock while holding parent's lock.
|
||||||
|
child.cancel(false, err) |
||||||
|
} |
||||||
|
c.children = nil |
||||||
|
c.mu.Unlock() |
||||||
|
|
||||||
|
if removeFromParent { |
||||||
|
removeChild(c.Context, c) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// WithDeadline returns a copy of the parent context with the deadline adjusted
|
||||||
|
// to be no later than d. If the parent's deadline is already earlier than d,
|
||||||
|
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
|
||||||
|
// context's Done channel is closed when the deadline expires, when the returned
|
||||||
|
// cancel function is called, or when the parent context's Done channel is
|
||||||
|
// closed, whichever happens first.
|
||||||
|
//
|
||||||
|
// Canceling this context releases resources associated with it, so code should
|
||||||
|
// call cancel as soon as the operations running in this Context complete.
|
||||||
|
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { |
||||||
|
if cur, ok := parent.Deadline(); ok && cur.Before(deadline) { |
||||||
|
// The current deadline is already sooner than the new one.
|
||||||
|
return WithCancel(parent) |
||||||
|
} |
||||||
|
c := &timerCtx{ |
||||||
|
cancelCtx: newCancelCtx(parent), |
||||||
|
deadline: deadline, |
||||||
|
} |
||||||
|
propagateCancel(parent, c) |
||||||
|
d := deadline.Sub(time.Now()) |
||||||
|
if d <= 0 { |
||||||
|
c.cancel(true, DeadlineExceeded) // deadline has already passed
|
||||||
|
return c, func() { c.cancel(true, Canceled) } |
||||||
|
} |
||||||
|
c.mu.Lock() |
||||||
|
defer c.mu.Unlock() |
||||||
|
if c.err == nil { |
||||||
|
c.timer = time.AfterFunc(d, func() { |
||||||
|
c.cancel(true, DeadlineExceeded) |
||||||
|
}) |
||||||
|
} |
||||||
|
return c, func() { c.cancel(true, Canceled) } |
||||||
|
} |
||||||
|
|
||||||
|
// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
|
||||||
|
// implement Done and Err. It implements cancel by stopping its timer then
|
||||||
|
// delegating to cancelCtx.cancel.
|
||||||
|
type timerCtx struct { |
||||||
|
*cancelCtx |
||||||
|
timer *time.Timer // Under cancelCtx.mu.
|
||||||
|
|
||||||
|
deadline time.Time |
||||||
|
} |
||||||
|
|
||||||
|
func (c *timerCtx) Deadline() (deadline time.Time, ok bool) { |
||||||
|
return c.deadline, true |
||||||
|
} |
||||||
|
|
||||||
|
func (c *timerCtx) String() string { |
||||||
|
return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now())) |
||||||
|
} |
||||||
|
|
||||||
|
func (c *timerCtx) cancel(removeFromParent bool, err error) { |
||||||
|
c.cancelCtx.cancel(false, err) |
||||||
|
if removeFromParent { |
||||||
|
// Remove this timerCtx from its parent cancelCtx's children.
|
||||||
|
removeChild(c.cancelCtx.Context, c) |
||||||
|
} |
||||||
|
c.mu.Lock() |
||||||
|
if c.timer != nil { |
||||||
|
c.timer.Stop() |
||||||
|
c.timer = nil |
||||||
|
} |
||||||
|
c.mu.Unlock() |
||||||
|
} |
||||||
|
|
||||||
|
// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
|
||||||
|
//
|
||||||
|
// Canceling this context releases resources associated with it, so code should
|
||||||
|
// call cancel as soon as the operations running in this Context complete:
|
||||||
|
//
|
||||||
|
// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
|
||||||
|
// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
|
||||||
|
// defer cancel() // releases resources if slowOperation completes before timeout elapses
|
||||||
|
// return slowOperation(ctx)
|
||||||
|
// }
|
||||||
|
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { |
||||||
|
return WithDeadline(parent, time.Now().Add(timeout)) |
||||||
|
} |
||||||
|
|
||||||
|
// WithValue returns a copy of parent in which the value associated with key is
|
||||||
|
// val.
|
||||||
|
//
|
||||||
|
// Use context Values only for request-scoped data that transits processes and
|
||||||
|
// APIs, not for passing optional parameters to functions.
|
||||||
|
func WithValue(parent Context, key interface{}, val interface{}) Context { |
||||||
|
return &valueCtx{parent, key, val} |
||||||
|
} |
||||||
|
|
||||||
|
// A valueCtx carries a key-value pair. It implements Value for that key and
|
||||||
|
// delegates all other calls to the embedded Context.
|
||||||
|
type valueCtx struct { |
||||||
|
Context |
||||||
|
key, val interface{} |
||||||
|
} |
||||||
|
|
||||||
|
func (c *valueCtx) String() string { |
||||||
|
return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val) |
||||||
|
} |
||||||
|
|
||||||
|
func (c *valueCtx) Value(key interface{}) interface{} { |
||||||
|
if c.key == key { |
||||||
|
return c.val |
||||||
|
} |
||||||
|
return c.Context.Value(key) |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects) |
||||||
|
*.o |
||||||
|
*.a |
||||||
|
*.so |
||||||
|
|
||||||
|
# Folders |
||||||
|
_obj |
||||||
|
_test |
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes |
||||||
|
*.[568vq] |
||||||
|
[568vq].out |
||||||
|
|
||||||
|
*.cgo1.go |
||||||
|
*.cgo2.c |
||||||
|
_cgo_defun.c |
||||||
|
_cgo_gotypes.go |
||||||
|
_cgo_export.* |
||||||
|
|
||||||
|
_testmain.go |
||||||
|
|
||||||
|
*.exe |
@ -0,0 +1,8 @@ |
|||||||
|
The MIT License (MIT) |
||||||
|
Copyright (c) 2013 npipe authors |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@ -0,0 +1,308 @@ |
|||||||
|
npipe [![Build status](https://ci.appveyor.com/api/projects/status/00vuepirsot29qwi)](https://ci.appveyor.com/project/natefinch/npipe) [![GoDoc](https://godoc.org/gopkg.in/natefinch/npipe.v2?status.svg)](https://godoc.org/gopkg.in/natefinch/npipe.v2) |
||||||
|
===== |
||||||
|
Package npipe provides a pure Go wrapper around Windows named pipes. |
||||||
|
|
||||||
|
Windows named pipe documentation: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365780 |
||||||
|
|
||||||
|
Note that the code lives at https://github.com/natefinch/npipe (v2 branch) |
||||||
|
but should be imported as gopkg.in/natefinch/npipe.v2 (the package name is |
||||||
|
still npipe). |
||||||
|
|
||||||
|
npipe provides an interface based on stdlib's net package, with Dial, Listen, |
||||||
|
and Accept functions, as well as associated implementations of net.Conn and |
||||||
|
net.Listener. It supports rpc over the connection. |
||||||
|
|
||||||
|
### Notes |
||||||
|
* Deadlines for reading/writing to the connection are only functional in Windows Vista/Server 2008 and above, due to limitations with the Windows API. |
||||||
|
|
||||||
|
* The pipes support byte mode only (no support for message mode) |
||||||
|
|
||||||
|
### Examples |
||||||
|
The Dial function connects a client to a named pipe: |
||||||
|
|
||||||
|
|
||||||
|
conn, err := npipe.Dial(`\\.\pipe\mypipename`) |
||||||
|
if err != nil { |
||||||
|
<handle error> |
||||||
|
} |
||||||
|
fmt.Fprintf(conn, "Hi server!\n") |
||||||
|
msg, err := bufio.NewReader(conn).ReadString('\n') |
||||||
|
... |
||||||
|
|
||||||
|
The Listen function creates servers: |
||||||
|
|
||||||
|
|
||||||
|
ln, err := npipe.Listen(`\\.\pipe\mypipename`) |
||||||
|
if err != nil { |
||||||
|
// handle error |
||||||
|
} |
||||||
|
for { |
||||||
|
conn, err := ln.Accept() |
||||||
|
if err != nil { |
||||||
|
// handle error |
||||||
|
continue |
||||||
|
} |
||||||
|
go handleConnection(conn) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Variables |
||||||
|
``` go |
||||||
|
var ErrClosed = PipeError{"Pipe has been closed.", false} |
||||||
|
``` |
||||||
|
ErrClosed is the error returned by PipeListener.Accept when Close is called |
||||||
|
on the PipeListener. |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## type PipeAddr |
||||||
|
``` go |
||||||
|
type PipeAddr string |
||||||
|
``` |
||||||
|
PipeAddr represents the address of a named pipe. |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### func (PipeAddr) Network |
||||||
|
``` go |
||||||
|
func (a PipeAddr) Network() string |
||||||
|
``` |
||||||
|
Network returns the address's network name, "pipe". |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### func (PipeAddr) String |
||||||
|
``` go |
||||||
|
func (a PipeAddr) String() string |
||||||
|
``` |
||||||
|
String returns the address of the pipe |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## type PipeConn |
||||||
|
``` go |
||||||
|
type PipeConn struct { |
||||||
|
// contains filtered or unexported fields |
||||||
|
} |
||||||
|
``` |
||||||
|
PipeConn is the implementation of the net.Conn interface for named pipe connections. |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### func Dial |
||||||
|
``` go |
||||||
|
func Dial(address string) (*PipeConn, error) |
||||||
|
``` |
||||||
|
Dial connects to a named pipe with the given address. If the specified pipe is not available, |
||||||
|
it will wait indefinitely for the pipe to become available. |
||||||
|
|
||||||
|
The address must be of the form \\.\\pipe\<name> for local pipes and \\<computer>\pipe\<name> |
||||||
|
for remote pipes. |
||||||
|
|
||||||
|
Dial will return a PipeError if you pass in a badly formatted pipe name. |
||||||
|
|
||||||
|
Examples: |
||||||
|
|
||||||
|
|
||||||
|
// local pipe |
||||||
|
conn, err := Dial(`\\.\pipe\mypipename`) |
||||||
|
|
||||||
|
// remote pipe |
||||||
|
conn, err := Dial(`\\othercomp\pipe\mypipename`) |
||||||
|
|
||||||
|
|
||||||
|
### func DialTimeout |
||||||
|
``` go |
||||||
|
func DialTimeout(address string, timeout time.Duration) (*PipeConn, error) |
||||||
|
``` |
||||||
|
DialTimeout acts like Dial, but will time out after the duration of timeout |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### func (\*PipeConn) Close |
||||||
|
``` go |
||||||
|
func (c *PipeConn) Close() error |
||||||
|
``` |
||||||
|
Close closes the connection. |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### func (\*PipeConn) LocalAddr |
||||||
|
``` go |
||||||
|
func (c *PipeConn) LocalAddr() net.Addr |
||||||
|
``` |
||||||
|
LocalAddr returns the local network address. |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### func (\*PipeConn) Read |
||||||
|
``` go |
||||||
|
func (c *PipeConn) Read(b []byte) (int, error) |
||||||
|
``` |
||||||
|
Read implements the net.Conn Read method. |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### func (\*PipeConn) RemoteAddr |
||||||
|
``` go |
||||||
|
func (c *PipeConn) RemoteAddr() net.Addr |
||||||
|
``` |
||||||
|
RemoteAddr returns the remote network address. |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### func (\*PipeConn) SetDeadline |
||||||
|
``` go |
||||||
|
func (c *PipeConn) SetDeadline(t time.Time) error |
||||||
|
``` |
||||||
|
SetDeadline implements the net.Conn SetDeadline method. |
||||||
|
Note that timeouts are only supported on Windows Vista/Server 2008 and above |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### func (\*PipeConn) SetReadDeadline |
||||||
|
``` go |
||||||
|
func (c *PipeConn) SetReadDeadline(t time.Time) error |
||||||
|
``` |
||||||
|
SetReadDeadline implements the net.Conn SetReadDeadline method. |
||||||
|
Note that timeouts are only supported on Windows Vista/Server 2008 and above |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### func (\*PipeConn) SetWriteDeadline |
||||||
|
``` go |
||||||
|
func (c *PipeConn) SetWriteDeadline(t time.Time) error |
||||||
|
``` |
||||||
|
SetWriteDeadline implements the net.Conn SetWriteDeadline method. |
||||||
|
Note that timeouts are only supported on Windows Vista/Server 2008 and above |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### func (\*PipeConn) Write |
||||||
|
``` go |
||||||
|
func (c *PipeConn) Write(b []byte) (int, error) |
||||||
|
``` |
||||||
|
Write implements the net.Conn Write method. |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## type PipeError |
||||||
|
``` go |
||||||
|
type PipeError struct { |
||||||
|
// contains filtered or unexported fields |
||||||
|
} |
||||||
|
``` |
||||||
|
PipeError is an error related to a call to a pipe |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### func (PipeError) Error |
||||||
|
``` go |
||||||
|
func (e PipeError) Error() string |
||||||
|
``` |
||||||
|
Error implements the error interface |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### func (PipeError) Temporary |
||||||
|
``` go |
||||||
|
func (e PipeError) Temporary() bool |
||||||
|
``` |
||||||
|
Temporary implements net.AddrError.Temporary() |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### func (PipeError) Timeout |
||||||
|
``` go |
||||||
|
func (e PipeError) Timeout() bool |
||||||
|
``` |
||||||
|
Timeout implements net.AddrError.Timeout() |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## type PipeListener |
||||||
|
``` go |
||||||
|
type PipeListener struct { |
||||||
|
// contains filtered or unexported fields |
||||||
|
} |
||||||
|
``` |
||||||
|
PipeListener is a named pipe listener. Clients should typically |
||||||
|
use variables of type net.Listener instead of assuming named pipe. |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### func Listen |
||||||
|
``` go |
||||||
|
func Listen(address string) (*PipeListener, error) |
||||||
|
``` |
||||||
|
Listen returns a new PipeListener that will listen on a pipe with the given |
||||||
|
address. The address must be of the form \\.\pipe\<name> |
||||||
|
|
||||||
|
Listen will return a PipeError for an incorrectly formatted pipe name. |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### func (\*PipeListener) Accept |
||||||
|
``` go |
||||||
|
func (l *PipeListener) Accept() (net.Conn, error) |
||||||
|
``` |
||||||
|
Accept implements the Accept method in the net.Listener interface; it |
||||||
|
waits for the next call and returns a generic net.Conn. |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### func (\*PipeListener) AcceptPipe |
||||||
|
``` go |
||||||
|
func (l *PipeListener) AcceptPipe() (*PipeConn, error) |
||||||
|
``` |
||||||
|
AcceptPipe accepts the next incoming call and returns the new connection. |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### func (\*PipeListener) Addr |
||||||
|
``` go |
||||||
|
func (l *PipeListener) Addr() net.Addr |
||||||
|
``` |
||||||
|
Addr returns the listener's network address, a PipeAddr. |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### func (\*PipeListener) Close |
||||||
|
``` go |
||||||
|
func (l *PipeListener) Close() error |
||||||
|
``` |
||||||
|
Close stops listening on the address. |
||||||
|
Already Accepted connections are not closed. |
@ -0,0 +1,50 @@ |
|||||||
|
// Copyright 2013 Nate Finch. All rights reserved.
|
||||||
|
// Use of this source code is governed by an MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package npipe provides a pure Go wrapper around Windows named pipes.
|
||||||
|
//
|
||||||
|
// !! Note, this package is Windows-only. There is no code to compile on linux.
|
||||||
|
//
|
||||||
|
// Windows named pipe documentation: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365780
|
||||||
|
//
|
||||||
|
// Note that the code lives at https://github.com/natefinch/npipe (v2 branch)
|
||||||
|
// but should be imported as gopkg.in/natefinch/npipe.v2 (the package name is
|
||||||
|
// still npipe).
|
||||||
|
//
|
||||||
|
// npipe provides an interface based on stdlib's net package, with Dial, Listen,
|
||||||
|
// and Accept functions, as well as associated implementations of net.Conn and
|
||||||
|
// net.Listener. It supports rpc over the connection.
|
||||||
|
//
|
||||||
|
// Notes
|
||||||
|
//
|
||||||
|
// * Deadlines for reading/writing to the connection are only functional in Windows Vista/Server 2008 and above, due to limitations with the Windows API.
|
||||||
|
//
|
||||||
|
// * The pipes support byte mode only (no support for message mode)
|
||||||
|
//
|
||||||
|
// Examples
|
||||||
|
//
|
||||||
|
// The Dial function connects a client to a named pipe:
|
||||||
|
// conn, err := npipe.Dial(`\\.\pipe\mypipename`)
|
||||||
|
// if err != nil {
|
||||||
|
// <handle error>
|
||||||
|
// }
|
||||||
|
// fmt.Fprintf(conn, "Hi server!\n")
|
||||||
|
// msg, err := bufio.NewReader(conn).ReadString('\n')
|
||||||
|
// ...
|
||||||
|
//
|
||||||
|
// The Listen function creates servers:
|
||||||
|
//
|
||||||
|
// ln, err := npipe.Listen(`\\.\pipe\mypipename`)
|
||||||
|
// if err != nil {
|
||||||
|
// // handle error
|
||||||
|
// }
|
||||||
|
// for {
|
||||||
|
// conn, err := ln.Accept()
|
||||||
|
// if err != nil {
|
||||||
|
// // handle error
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// go handleConnection(conn)
|
||||||
|
// }
|
||||||
|
package npipe |
@ -0,0 +1,531 @@ |
|||||||
|
package npipe |
||||||
|
|
||||||
|
//sys createNamedPipe(name *uint16, openMode uint32, pipeMode uint32, maxInstances uint32, outBufSize uint32, inBufSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW
|
||||||
|
//sys connectNamedPipe(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) = ConnectNamedPipe
|
||||||
|
//sys disconnectNamedPipe(handle syscall.Handle) (err error) = DisconnectNamedPipe
|
||||||
|
//sys waitNamedPipe(name *uint16, timeout uint32) (err error) = WaitNamedPipeW
|
||||||
|
//sys createEvent(sa *syscall.SecurityAttributes, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateEventW
|
||||||
|
//sys getOverlappedResult(handle syscall.Handle, overlapped *syscall.Overlapped, transferred *uint32, wait bool) (err error) = GetOverlappedResult
|
||||||
|
//sys cancelIoEx(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) = CancelIoEx
|
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
"net" |
||||||
|
"sync" |
||||||
|
"syscall" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
// openMode
|
||||||
|
pipe_access_duplex = 0x3 |
||||||
|
pipe_access_inbound = 0x1 |
||||||
|
pipe_access_outbound = 0x2 |
||||||
|
|
||||||
|
// openMode write flags
|
||||||
|
file_flag_first_pipe_instance = 0x00080000 |
||||||
|
file_flag_write_through = 0x80000000 |
||||||
|
file_flag_overlapped = 0x40000000 |
||||||
|
|
||||||
|
// openMode ACL flags
|
||||||
|
write_dac = 0x00040000 |
||||||
|
write_owner = 0x00080000 |
||||||
|
access_system_security = 0x01000000 |
||||||
|
|
||||||
|
// pipeMode
|
||||||
|
pipe_type_byte = 0x0 |
||||||
|
pipe_type_message = 0x4 |
||||||
|
|
||||||
|
// pipeMode read mode flags
|
||||||
|
pipe_readmode_byte = 0x0 |
||||||
|
pipe_readmode_message = 0x2 |
||||||
|
|
||||||
|
// pipeMode wait mode flags
|
||||||
|
pipe_wait = 0x0 |
||||||
|
pipe_nowait = 0x1 |
||||||
|
|
||||||
|
// pipeMode remote-client mode flags
|
||||||
|
pipe_accept_remote_clients = 0x0 |
||||||
|
pipe_reject_remote_clients = 0x8 |
||||||
|
|
||||||
|
pipe_unlimited_instances = 255 |
||||||
|
|
||||||
|
nmpwait_wait_forever = 0xFFFFFFFF |
||||||
|
|
||||||
|
// the two not-an-errors below occur if a client connects to the pipe between
|
||||||
|
// the server's CreateNamedPipe and ConnectNamedPipe calls.
|
||||||
|
error_no_data syscall.Errno = 0xE8 |
||||||
|
error_pipe_connected syscall.Errno = 0x217 |
||||||
|
error_pipe_busy syscall.Errno = 0xE7 |
||||||
|
error_sem_timeout syscall.Errno = 0x79 |
||||||
|
|
||||||
|
error_bad_pathname syscall.Errno = 0xA1 |
||||||
|
error_invalid_name syscall.Errno = 0x7B |
||||||
|
|
||||||
|
error_io_incomplete syscall.Errno = 0x3e4 |
||||||
|
) |
||||||
|
|
||||||
|
var _ net.Conn = (*PipeConn)(nil) |
||||||
|
var _ net.Listener = (*PipeListener)(nil) |
||||||
|
|
||||||
|
// ErrClosed is the error returned by PipeListener.Accept when Close is called
|
||||||
|
// on the PipeListener.
|
||||||
|
var ErrClosed = PipeError{"Pipe has been closed.", false} |
||||||
|
|
||||||
|
// PipeError is an error related to a call to a pipe
|
||||||
|
type PipeError struct { |
||||||
|
msg string |
||||||
|
timeout bool |
||||||
|
} |
||||||
|
|
||||||
|
// Error implements the error interface
|
||||||
|
func (e PipeError) Error() string { |
||||||
|
return e.msg |
||||||
|
} |
||||||
|
|
||||||
|
// Timeout implements net.AddrError.Timeout()
|
||||||
|
func (e PipeError) Timeout() bool { |
||||||
|
return e.timeout |
||||||
|
} |
||||||
|
|
||||||
|
// Temporary implements net.AddrError.Temporary()
|
||||||
|
func (e PipeError) Temporary() bool { |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
// Dial connects to a named pipe with the given address. If the specified pipe is not available,
|
||||||
|
// it will wait indefinitely for the pipe to become available.
|
||||||
|
//
|
||||||
|
// The address must be of the form \\.\\pipe\<name> for local pipes and \\<computer>\pipe\<name>
|
||||||
|
// for remote pipes.
|
||||||
|
//
|
||||||
|
// Dial will return a PipeError if you pass in a badly formatted pipe name.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
// // local pipe
|
||||||
|
// conn, err := Dial(`\\.\pipe\mypipename`)
|
||||||
|
//
|
||||||
|
// // remote pipe
|
||||||
|
// conn, err := Dial(`\\othercomp\pipe\mypipename`)
|
||||||
|
func Dial(address string) (*PipeConn, error) { |
||||||
|
for { |
||||||
|
conn, err := dial(address, nmpwait_wait_forever) |
||||||
|
if err == nil { |
||||||
|
return conn, nil |
||||||
|
} |
||||||
|
if isPipeNotReady(err) { |
||||||
|
<-time.After(100 * time.Millisecond) |
||||||
|
continue |
||||||
|
} |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// DialTimeout acts like Dial, but will time out after the duration of timeout
|
||||||
|
func DialTimeout(address string, timeout time.Duration) (*PipeConn, error) { |
||||||
|
deadline := time.Now().Add(timeout) |
||||||
|
|
||||||
|
now := time.Now() |
||||||
|
for now.Before(deadline) { |
||||||
|
millis := uint32(deadline.Sub(now) / time.Millisecond) |
||||||
|
conn, err := dial(address, millis) |
||||||
|
if err == nil { |
||||||
|
return conn, nil |
||||||
|
} |
||||||
|
if err == error_sem_timeout { |
||||||
|
// This is WaitNamedPipe's timeout error, so we know we're done
|
||||||
|
return nil, PipeError{fmt.Sprintf( |
||||||
|
"Timed out waiting for pipe '%s' to come available", address), true} |
||||||
|
} |
||||||
|
if isPipeNotReady(err) { |
||||||
|
left := deadline.Sub(time.Now()) |
||||||
|
retry := 100 * time.Millisecond |
||||||
|
if left > retry { |
||||||
|
<-time.After(retry) |
||||||
|
} else { |
||||||
|
<-time.After(left - time.Millisecond) |
||||||
|
} |
||||||
|
now = time.Now() |
||||||
|
continue |
||||||
|
} |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return nil, PipeError{fmt.Sprintf( |
||||||
|
"Timed out waiting for pipe '%s' to come available", address), true} |
||||||
|
} |
||||||
|
|
||||||
|
// isPipeNotReady checks the error to see if it indicates the pipe is not ready
|
||||||
|
func isPipeNotReady(err error) bool { |
||||||
|
// Pipe Busy means another client just grabbed the open pipe end,
|
||||||
|
// and the server hasn't made a new one yet.
|
||||||
|
// File Not Found means the server hasn't created the pipe yet.
|
||||||
|
// Neither is a fatal error.
|
||||||
|
|
||||||
|
return err == syscall.ERROR_FILE_NOT_FOUND || err == error_pipe_busy |
||||||
|
} |
||||||
|
|
||||||
|
// newOverlapped creates a structure used to track asynchronous
|
||||||
|
// I/O requests that have been issued.
|
||||||
|
func newOverlapped() (*syscall.Overlapped, error) { |
||||||
|
event, err := createEvent(nil, true, true, nil) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return &syscall.Overlapped{HEvent: event}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// waitForCompletion waits for an asynchronous I/O request referred to by overlapped to complete.
|
||||||
|
// This function returns the number of bytes transferred by the operation and an error code if
|
||||||
|
// applicable (nil otherwise).
|
||||||
|
func waitForCompletion(handle syscall.Handle, overlapped *syscall.Overlapped) (uint32, error) { |
||||||
|
_, err := syscall.WaitForSingleObject(overlapped.HEvent, syscall.INFINITE) |
||||||
|
if err != nil { |
||||||
|
return 0, err |
||||||
|
} |
||||||
|
var transferred uint32 |
||||||
|
err = getOverlappedResult(handle, overlapped, &transferred, true) |
||||||
|
return transferred, err |
||||||
|
} |
||||||
|
|
||||||
|
// dial is a helper to initiate a connection to a named pipe that has been started by a server.
|
||||||
|
// The timeout is only enforced if the pipe server has already created the pipe, otherwise
|
||||||
|
// this function will return immediately.
|
||||||
|
func dial(address string, timeout uint32) (*PipeConn, error) { |
||||||
|
name, err := syscall.UTF16PtrFromString(string(address)) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
// If at least one instance of the pipe has been created, this function
|
||||||
|
// will wait timeout milliseconds for it to become available.
|
||||||
|
// It will return immediately regardless of timeout, if no instances
|
||||||
|
// of the named pipe have been created yet.
|
||||||
|
// If this returns with no error, there is a pipe available.
|
||||||
|
if err := waitNamedPipe(name, timeout); err != nil { |
||||||
|
if err == error_bad_pathname { |
||||||
|
// badly formatted pipe name
|
||||||
|
return nil, badAddr(address) |
||||||
|
} |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
pathp, err := syscall.UTF16PtrFromString(address) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
handle, err := syscall.CreateFile(pathp, syscall.GENERIC_READ|syscall.GENERIC_WRITE, |
||||||
|
uint32(syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE), nil, syscall.OPEN_EXISTING, |
||||||
|
syscall.FILE_FLAG_OVERLAPPED, 0) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return &PipeConn{handle: handle, addr: PipeAddr(address)}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Listen returns a new PipeListener that will listen on a pipe with the given
|
||||||
|
// address. The address must be of the form \\.\pipe\<name>
|
||||||
|
//
|
||||||
|
// Listen will return a PipeError for an incorrectly formatted pipe name.
|
||||||
|
func Listen(address string) (*PipeListener, error) { |
||||||
|
handle, err := createPipe(address, true) |
||||||
|
if err == error_invalid_name { |
||||||
|
return nil, badAddr(address) |
||||||
|
} |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
return &PipeListener{ |
||||||
|
addr: PipeAddr(address), |
||||||
|
handle: handle, |
||||||
|
}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// PipeListener is a named pipe listener. Clients should typically
|
||||||
|
// use variables of type net.Listener instead of assuming named pipe.
|
||||||
|
type PipeListener struct { |
||||||
|
mu sync.Mutex |
||||||
|
|
||||||
|
addr PipeAddr |
||||||
|
handle syscall.Handle |
||||||
|
closed bool |
||||||
|
|
||||||
|
// acceptHandle contains the current handle waiting for
|
||||||
|
// an incoming connection or nil.
|
||||||
|
acceptHandle syscall.Handle |
||||||
|
// acceptOverlapped is set before waiting on a connection.
|
||||||
|
// If not waiting, it is nil.
|
||||||
|
acceptOverlapped *syscall.Overlapped |
||||||
|
} |
||||||
|
|
||||||
|
// Accept implements the Accept method in the net.Listener interface; it
|
||||||
|
// waits for the next call and returns a generic net.Conn.
|
||||||
|
func (l *PipeListener) Accept() (net.Conn, error) { |
||||||
|
c, err := l.AcceptPipe() |
||||||
|
for err == error_no_data { |
||||||
|
// Ignore clients that connect and immediately disconnect.
|
||||||
|
c, err = l.AcceptPipe() |
||||||
|
} |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return c, nil |
||||||
|
} |
||||||
|
|
||||||
|
// AcceptPipe accepts the next incoming call and returns the new connection.
|
||||||
|
// It might return an error if a client connected and immediately cancelled
|
||||||
|
// the connection.
|
||||||
|
func (l *PipeListener) AcceptPipe() (*PipeConn, error) { |
||||||
|
if l == nil { |
||||||
|
return nil, syscall.EINVAL |
||||||
|
} |
||||||
|
|
||||||
|
l.mu.Lock() |
||||||
|
defer l.mu.Unlock() |
||||||
|
|
||||||
|
if l.addr == "" || l.closed { |
||||||
|
return nil, syscall.EINVAL |
||||||
|
} |
||||||
|
|
||||||
|
// the first time we call accept, the handle will have been created by the Listen
|
||||||
|
// call. This is to prevent race conditions where the client thinks the server
|
||||||
|
// isn't listening because it hasn't actually called create yet. After the first time, we'll
|
||||||
|
// have to create a new handle each time
|
||||||
|
handle := l.handle |
||||||
|
if handle == 0 { |
||||||
|
var err error |
||||||
|
handle, err = createPipe(string(l.addr), false) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
} else { |
||||||
|
l.handle = 0 |
||||||
|
} |
||||||
|
|
||||||
|
overlapped, err := newOverlapped() |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
defer syscall.CloseHandle(overlapped.HEvent) |
||||||
|
err = connectNamedPipe(handle, overlapped) |
||||||
|
if err == nil || err == error_pipe_connected { |
||||||
|
return &PipeConn{handle: handle, addr: l.addr}, nil |
||||||
|
} |
||||||
|
|
||||||
|
if err == error_io_incomplete || err == syscall.ERROR_IO_PENDING { |
||||||
|
l.acceptOverlapped = overlapped |
||||||
|
l.acceptHandle = handle |
||||||
|
// unlock here so close can function correctly while we wait (we'll
|
||||||
|
// get relocked via the defer below, before the original defer
|
||||||
|
// unlock happens.)
|
||||||
|
l.mu.Unlock() |
||||||
|
defer func() { |
||||||
|
l.mu.Lock() |
||||||
|
l.acceptOverlapped = nil |
||||||
|
l.acceptHandle = 0 |
||||||
|
// unlock is via defer above.
|
||||||
|
}() |
||||||
|
_, err = waitForCompletion(handle, overlapped) |
||||||
|
} |
||||||
|
if err == syscall.ERROR_OPERATION_ABORTED { |
||||||
|
// Return error compatible to net.Listener.Accept() in case the
|
||||||
|
// listener was closed.
|
||||||
|
return nil, ErrClosed |
||||||
|
} |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return &PipeConn{handle: handle, addr: l.addr}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Close stops listening on the address.
|
||||||
|
// Already Accepted connections are not closed.
|
||||||
|
func (l *PipeListener) Close() error { |
||||||
|
l.mu.Lock() |
||||||
|
defer l.mu.Unlock() |
||||||
|
|
||||||
|
if l.closed { |
||||||
|
return nil |
||||||
|
} |
||||||
|
l.closed = true |
||||||
|
if l.handle != 0 { |
||||||
|
err := disconnectNamedPipe(l.handle) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
err = syscall.CloseHandle(l.handle) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
l.handle = 0 |
||||||
|
} |
||||||
|
if l.acceptOverlapped != nil && l.acceptHandle != 0 { |
||||||
|
// Cancel the pending IO. This call does not block, so it is safe
|
||||||
|
// to hold onto the mutex above.
|
||||||
|
if err := cancelIoEx(l.acceptHandle, l.acceptOverlapped); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
err := syscall.CloseHandle(l.acceptOverlapped.HEvent) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
l.acceptOverlapped.HEvent = 0 |
||||||
|
err = syscall.CloseHandle(l.acceptHandle) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
l.acceptHandle = 0 |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Addr returns the listener's network address, a PipeAddr.
|
||||||
|
func (l *PipeListener) Addr() net.Addr { return l.addr } |
||||||
|
|
||||||
|
// PipeConn is the implementation of the net.Conn interface for named pipe connections.
|
||||||
|
type PipeConn struct { |
||||||
|
handle syscall.Handle |
||||||
|
addr PipeAddr |
||||||
|
|
||||||
|
// these aren't actually used yet
|
||||||
|
readDeadline *time.Time |
||||||
|
writeDeadline *time.Time |
||||||
|
} |
||||||
|
|
||||||
|
type iodata struct { |
||||||
|
n uint32 |
||||||
|
err error |
||||||
|
} |
||||||
|
|
||||||
|
// completeRequest looks at iodata to see if a request is pending. If so, it waits for it to either complete or to
|
||||||
|
// abort due to hitting the specified deadline. Deadline may be set to nil to wait forever. If no request is pending,
|
||||||
|
// the content of iodata is returned.
|
||||||
|
func (c *PipeConn) completeRequest(data iodata, deadline *time.Time, overlapped *syscall.Overlapped) (int, error) { |
||||||
|
if data.err == error_io_incomplete || data.err == syscall.ERROR_IO_PENDING { |
||||||
|
var timer <-chan time.Time |
||||||
|
if deadline != nil { |
||||||
|
if timeDiff := deadline.Sub(time.Now()); timeDiff > 0 { |
||||||
|
timer = time.After(timeDiff) |
||||||
|
} |
||||||
|
} |
||||||
|
done := make(chan iodata) |
||||||
|
go func() { |
||||||
|
n, err := waitForCompletion(c.handle, overlapped) |
||||||
|
done <- iodata{n, err} |
||||||
|
}() |
||||||
|
select { |
||||||
|
case data = <-done: |
||||||
|
case <-timer: |
||||||
|
syscall.CancelIoEx(c.handle, overlapped) |
||||||
|
data = iodata{0, timeout(c.addr.String())} |
||||||
|
} |
||||||
|
} |
||||||
|
// Windows will produce ERROR_BROKEN_PIPE upon closing
|
||||||
|
// a handle on the other end of a connection. Go RPC
|
||||||
|
// expects an io.EOF error in this case.
|
||||||
|
if data.err == syscall.ERROR_BROKEN_PIPE { |
||||||
|
data.err = io.EOF |
||||||
|
} |
||||||
|
return int(data.n), data.err |
||||||
|
} |
||||||
|
|
||||||
|
// Read implements the net.Conn Read method.
|
||||||
|
func (c *PipeConn) Read(b []byte) (int, error) { |
||||||
|
// Use ReadFile() rather than Read() because the latter
|
||||||
|
// contains a workaround that eats ERROR_BROKEN_PIPE.
|
||||||
|
overlapped, err := newOverlapped() |
||||||
|
if err != nil { |
||||||
|
return 0, err |
||||||
|
} |
||||||
|
defer syscall.CloseHandle(overlapped.HEvent) |
||||||
|
var n uint32 |
||||||
|
err = syscall.ReadFile(c.handle, b, &n, overlapped) |
||||||
|
return c.completeRequest(iodata{n, err}, c.readDeadline, overlapped) |
||||||
|
} |
||||||
|
|
||||||
|
// Write implements the net.Conn Write method.
|
||||||
|
func (c *PipeConn) Write(b []byte) (int, error) { |
||||||
|
overlapped, err := newOverlapped() |
||||||
|
if err != nil { |
||||||
|
return 0, err |
||||||
|
} |
||||||
|
defer syscall.CloseHandle(overlapped.HEvent) |
||||||
|
var n uint32 |
||||||
|
err = syscall.WriteFile(c.handle, b, &n, overlapped) |
||||||
|
return c.completeRequest(iodata{n, err}, c.writeDeadline, overlapped) |
||||||
|
} |
||||||
|
|
||||||
|
// Close closes the connection.
|
||||||
|
func (c *PipeConn) Close() error { |
||||||
|
return syscall.CloseHandle(c.handle) |
||||||
|
} |
||||||
|
|
||||||
|
// LocalAddr returns the local network address.
|
||||||
|
func (c *PipeConn) LocalAddr() net.Addr { |
||||||
|
return c.addr |
||||||
|
} |
||||||
|
|
||||||
|
// RemoteAddr returns the remote network address.
|
||||||
|
func (c *PipeConn) RemoteAddr() net.Addr { |
||||||
|
// not sure what to do here, we don't have remote addr....
|
||||||
|
return c.addr |
||||||
|
} |
||||||
|
|
||||||
|
// SetDeadline implements the net.Conn SetDeadline method.
|
||||||
|
// Note that timeouts are only supported on Windows Vista/Server 2008 and above
|
||||||
|
func (c *PipeConn) SetDeadline(t time.Time) error { |
||||||
|
c.SetReadDeadline(t) |
||||||
|
c.SetWriteDeadline(t) |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// SetReadDeadline implements the net.Conn SetReadDeadline method.
|
||||||
|
// Note that timeouts are only supported on Windows Vista/Server 2008 and above
|
||||||
|
func (c *PipeConn) SetReadDeadline(t time.Time) error { |
||||||
|
c.readDeadline = &t |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// SetWriteDeadline implements the net.Conn SetWriteDeadline method.
|
||||||
|
// Note that timeouts are only supported on Windows Vista/Server 2008 and above
|
||||||
|
func (c *PipeConn) SetWriteDeadline(t time.Time) error { |
||||||
|
c.writeDeadline = &t |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// PipeAddr represents the address of a named pipe.
|
||||||
|
type PipeAddr string |
||||||
|
|
||||||
|
// Network returns the address's network name, "pipe".
|
||||||
|
func (a PipeAddr) Network() string { return "pipe" } |
||||||
|
|
||||||
|
// String returns the address of the pipe
|
||||||
|
func (a PipeAddr) String() string { |
||||||
|
return string(a) |
||||||
|
} |
||||||
|
|
||||||
|
// createPipe is a helper function to make sure we always create pipes
|
||||||
|
// with the same arguments, since subsequent calls to create pipe need
|
||||||
|
// to use the same arguments as the first one. If first is set, fail
|
||||||
|
// if the pipe already exists.
|
||||||
|
func createPipe(address string, first bool) (syscall.Handle, error) { |
||||||
|
n, err := syscall.UTF16PtrFromString(address) |
||||||
|
if err != nil { |
||||||
|
return 0, err |
||||||
|
} |
||||||
|
mode := uint32(pipe_access_duplex | syscall.FILE_FLAG_OVERLAPPED) |
||||||
|
if first { |
||||||
|
mode |= file_flag_first_pipe_instance |
||||||
|
} |
||||||
|
return createNamedPipe(n, |
||||||
|
mode, |
||||||
|
pipe_type_byte, |
||||||
|
pipe_unlimited_instances, |
||||||
|
512, 512, 0, nil) |
||||||
|
} |
||||||
|
|
||||||
|
func badAddr(addr string) PipeError { |
||||||
|
return PipeError{fmt.Sprintf("Invalid pipe address '%s'.", addr), false} |
||||||
|
} |
||||||
|
func timeout(addr string) PipeError { |
||||||
|
return PipeError{fmt.Sprintf("Pipe IO timed out waiting for '%s'", addr), true} |
||||||
|
} |
@ -0,0 +1,124 @@ |
|||||||
|
// +build windows
|
||||||
|
// go build mksyscall_windows.go && ./mksyscall_windows npipe_windows.go
|
||||||
|
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||||
|
|
||||||
|
package npipe |
||||||
|
|
||||||
|
import "unsafe" |
||||||
|
import "syscall" |
||||||
|
|
||||||
|
var ( |
||||||
|
modkernel32 = syscall.NewLazyDLL("kernel32.dll") |
||||||
|
|
||||||
|
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW") |
||||||
|
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe") |
||||||
|
procDisconnectNamedPipe = modkernel32.NewProc("DisconnectNamedPipe") |
||||||
|
procWaitNamedPipeW = modkernel32.NewProc("WaitNamedPipeW") |
||||||
|
procCreateEventW = modkernel32.NewProc("CreateEventW") |
||||||
|
procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult") |
||||||
|
procCancelIoEx = modkernel32.NewProc("CancelIoEx") |
||||||
|
) |
||||||
|
|
||||||
|
func createNamedPipe(name *uint16, openMode uint32, pipeMode uint32, maxInstances uint32, outBufSize uint32, inBufSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) { |
||||||
|
r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(openMode), uintptr(pipeMode), uintptr(maxInstances), uintptr(outBufSize), uintptr(inBufSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0) |
||||||
|
handle = syscall.Handle(r0) |
||||||
|
if handle == syscall.InvalidHandle { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func cancelIoEx(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) { |
||||||
|
r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), 0) |
||||||
|
if r1 == 0 { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func connectNamedPipe(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) { |
||||||
|
r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), 0) |
||||||
|
if r1 == 0 { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func disconnectNamedPipe(handle syscall.Handle) (err error) { |
||||||
|
r1, _, e1 := syscall.Syscall(procDisconnectNamedPipe.Addr(), 1, uintptr(handle), 0, 0) |
||||||
|
if r1 == 0 { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func waitNamedPipe(name *uint16, timeout uint32) (err error) { |
||||||
|
r1, _, e1 := syscall.Syscall(procWaitNamedPipeW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(timeout), 0) |
||||||
|
if r1 == 0 { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func createEvent(sa *syscall.SecurityAttributes, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) { |
||||||
|
var _p0 uint32 |
||||||
|
if manualReset { |
||||||
|
_p0 = 1 |
||||||
|
} else { |
||||||
|
_p0 = 0 |
||||||
|
} |
||||||
|
var _p1 uint32 |
||||||
|
if initialState { |
||||||
|
_p1 = 1 |
||||||
|
} else { |
||||||
|
_p1 = 0 |
||||||
|
} |
||||||
|
r0, _, e1 := syscall.Syscall6(procCreateEventW.Addr(), 4, uintptr(unsafe.Pointer(sa)), uintptr(_p0), uintptr(_p1), uintptr(unsafe.Pointer(name)), 0, 0) |
||||||
|
handle = syscall.Handle(r0) |
||||||
|
if handle == syscall.InvalidHandle { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func getOverlappedResult(handle syscall.Handle, overlapped *syscall.Overlapped, transferred *uint32, wait bool) (err error) { |
||||||
|
var _p0 uint32 |
||||||
|
if wait { |
||||||
|
_p0 = 1 |
||||||
|
} else { |
||||||
|
_p0 = 0 |
||||||
|
} |
||||||
|
r1, _, e1 := syscall.Syscall6(procGetOverlappedResult.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(transferred)), uintptr(_p0), 0, 0) |
||||||
|
if r1 == 0 { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
@ -0,0 +1,124 @@ |
|||||||
|
// +build windows
|
||||||
|
// go build mksyscall_windows.go && ./mksyscall_windows npipe_windows.go
|
||||||
|
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||||
|
|
||||||
|
package npipe |
||||||
|
|
||||||
|
import "unsafe" |
||||||
|
import "syscall" |
||||||
|
|
||||||
|
var ( |
||||||
|
modkernel32 = syscall.NewLazyDLL("kernel32.dll") |
||||||
|
|
||||||
|
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW") |
||||||
|
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe") |
||||||
|
procDisconnectNamedPipe = modkernel32.NewProc("DisconnectNamedPipe") |
||||||
|
procWaitNamedPipeW = modkernel32.NewProc("WaitNamedPipeW") |
||||||
|
procCreateEventW = modkernel32.NewProc("CreateEventW") |
||||||
|
procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult") |
||||||
|
procCancelIoEx = modkernel32.NewProc("CancelIoEx") |
||||||
|
) |
||||||
|
|
||||||
|
func createNamedPipe(name *uint16, openMode uint32, pipeMode uint32, maxInstances uint32, outBufSize uint32, inBufSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) { |
||||||
|
r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(openMode), uintptr(pipeMode), uintptr(maxInstances), uintptr(outBufSize), uintptr(inBufSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0) |
||||||
|
handle = syscall.Handle(r0) |
||||||
|
if handle == syscall.InvalidHandle { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func cancelIoEx(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) { |
||||||
|
r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), 0) |
||||||
|
if r1 == 0 { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func connectNamedPipe(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) { |
||||||
|
r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), 0) |
||||||
|
if r1 == 0 { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func disconnectNamedPipe(handle syscall.Handle) (err error) { |
||||||
|
r1, _, e1 := syscall.Syscall(procDisconnectNamedPipe.Addr(), 1, uintptr(handle), 0, 0) |
||||||
|
if r1 == 0 { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func waitNamedPipe(name *uint16, timeout uint32) (err error) { |
||||||
|
r1, _, e1 := syscall.Syscall(procWaitNamedPipeW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(timeout), 0) |
||||||
|
if r1 == 0 { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func createEvent(sa *syscall.SecurityAttributes, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) { |
||||||
|
var _p0 uint32 |
||||||
|
if manualReset { |
||||||
|
_p0 = 1 |
||||||
|
} else { |
||||||
|
_p0 = 0 |
||||||
|
} |
||||||
|
var _p1 uint32 |
||||||
|
if initialState { |
||||||
|
_p1 = 1 |
||||||
|
} else { |
||||||
|
_p1 = 0 |
||||||
|
} |
||||||
|
r0, _, e1 := syscall.Syscall6(procCreateEventW.Addr(), 4, uintptr(unsafe.Pointer(sa)), uintptr(_p0), uintptr(_p1), uintptr(unsafe.Pointer(name)), 0, 0) |
||||||
|
handle = syscall.Handle(r0) |
||||||
|
if handle == syscall.InvalidHandle { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func getOverlappedResult(handle syscall.Handle, overlapped *syscall.Overlapped, transferred *uint32, wait bool) (err error) { |
||||||
|
var _p0 uint32 |
||||||
|
if wait { |
||||||
|
_p0 = 1 |
||||||
|
} else { |
||||||
|
_p0 = 0 |
||||||
|
} |
||||||
|
r1, _, e1 := syscall.Syscall6(procGetOverlappedResult.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(transferred)), uintptr(_p0), 0, 0) |
||||||
|
if r1 == 0 { |
||||||
|
if e1 != 0 { |
||||||
|
err = error(e1) |
||||||
|
} else { |
||||||
|
err = syscall.EINVAL |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
Loading…
Reference in new issue