Write an openapi spec for the worker API and use `deepmap/oapi-codegen`
to generate scaffolding for the server-side using the `labstack/echo`
server.
Incidentally, echo by default returns the errors in the same format that
worker API always has:
{ "message": "..." }
The API itself is unchanged to make this change easier to understand. It
will be changed to better suit our needs in future commits.
125 lines
3.3 KiB
Go
125 lines
3.3 KiB
Go
// +build windows
|
|
// +build !appengine
|
|
|
|
package isatty
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"syscall"
|
|
"unicode/utf16"
|
|
"unsafe"
|
|
)
|
|
|
|
const (
|
|
objectNameInfo uintptr = 1
|
|
fileNameInfo = 2
|
|
fileTypePipe = 3
|
|
)
|
|
|
|
var (
|
|
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
|
ntdll = syscall.NewLazyDLL("ntdll.dll")
|
|
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
|
|
procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx")
|
|
procGetFileType = kernel32.NewProc("GetFileType")
|
|
procNtQueryObject = ntdll.NewProc("NtQueryObject")
|
|
)
|
|
|
|
func init() {
|
|
// Check if GetFileInformationByHandleEx is available.
|
|
if procGetFileInformationByHandleEx.Find() != nil {
|
|
procGetFileInformationByHandleEx = nil
|
|
}
|
|
}
|
|
|
|
// IsTerminal return true if the file descriptor is terminal.
|
|
func IsTerminal(fd uintptr) bool {
|
|
var st uint32
|
|
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0)
|
|
return r != 0 && e == 0
|
|
}
|
|
|
|
// Check pipe name is used for cygwin/msys2 pty.
|
|
// Cygwin/MSYS2 PTY has a name like:
|
|
// \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master
|
|
func isCygwinPipeName(name string) bool {
|
|
token := strings.Split(name, "-")
|
|
if len(token) < 5 {
|
|
return false
|
|
}
|
|
|
|
if token[0] != `\msys` &&
|
|
token[0] != `\cygwin` &&
|
|
token[0] != `\Device\NamedPipe\msys` &&
|
|
token[0] != `\Device\NamedPipe\cygwin` {
|
|
return false
|
|
}
|
|
|
|
if token[1] == "" {
|
|
return false
|
|
}
|
|
|
|
if !strings.HasPrefix(token[2], "pty") {
|
|
return false
|
|
}
|
|
|
|
if token[3] != `from` && token[3] != `to` {
|
|
return false
|
|
}
|
|
|
|
if token[4] != "master" {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// getFileNameByHandle use the undocomented ntdll NtQueryObject to get file full name from file handler
|
|
// since GetFileInformationByHandleEx is not avilable under windows Vista and still some old fashion
|
|
// guys are using Windows XP, this is a workaround for those guys, it will also work on system from
|
|
// Windows vista to 10
|
|
// see https://stackoverflow.com/a/18792477 for details
|
|
func getFileNameByHandle(fd uintptr) (string, error) {
|
|
if procNtQueryObject == nil {
|
|
return "", errors.New("ntdll.dll: NtQueryObject not supported")
|
|
}
|
|
|
|
var buf [4 + syscall.MAX_PATH]uint16
|
|
var result int
|
|
r, _, e := syscall.Syscall6(procNtQueryObject.Addr(), 5,
|
|
fd, objectNameInfo, uintptr(unsafe.Pointer(&buf)), uintptr(2*len(buf)), uintptr(unsafe.Pointer(&result)), 0)
|
|
if r != 0 {
|
|
return "", e
|
|
}
|
|
return string(utf16.Decode(buf[4 : 4+buf[0]/2])), nil
|
|
}
|
|
|
|
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
|
|
// terminal.
|
|
func IsCygwinTerminal(fd uintptr) bool {
|
|
if procGetFileInformationByHandleEx == nil {
|
|
name, err := getFileNameByHandle(fd)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return isCygwinPipeName(name)
|
|
}
|
|
|
|
// Cygwin/msys's pty is a pipe.
|
|
ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0)
|
|
if ft != fileTypePipe || e != 0 {
|
|
return false
|
|
}
|
|
|
|
var buf [2 + syscall.MAX_PATH]uint16
|
|
r, _, e := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(),
|
|
4, fd, fileNameInfo, uintptr(unsafe.Pointer(&buf)),
|
|
uintptr(len(buf)*2), 0, 0)
|
|
if r == 0 || e != 0 {
|
|
return false
|
|
}
|
|
|
|
l := *(*uint32)(unsafe.Pointer(&buf))
|
|
return isCygwinPipeName(string(utf16.Decode(buf[2 : 2+l/2])))
|
|
}
|