log: update windows redirection code to use x/sys/windows and include dup

This modernises the Windows redirect code to use x/sys/windows and to
dup os.Stderr for use in the terminal.
This commit is contained in:
Nick Craig-Wood 2022-10-07 16:10:30 +01:00
parent 16b383e18f
commit 3450d049b5
2 changed files with 34 additions and 17 deletions

View File

@ -12,29 +12,43 @@ package log
import (
"log"
"os"
"syscall"
"github.com/rclone/rclone/lib/terminal"
"golang.org/x/sys/windows"
)
var (
kernel32 = syscall.MustLoadDLL("kernel32.dll")
procSetStdHandle = kernel32.MustFindProc("SetStdHandle")
)
func setStdHandle(stdhandle int32, handle syscall.Handle) error {
r0, _, e1 := syscall.Syscall(procSetStdHandle.Addr(), 2, uintptr(stdhandle), uintptr(handle), 0)
if r0 == 0 {
if e1 != 0 {
return error(e1)
}
return syscall.EINVAL
// dup oldfd creating a functional copy as newfd
// conceptually the same as the unix `dup()` function
func dup(oldfd uintptr) (newfd uintptr, err error) {
var (
newfdHandle windows.Handle
processHandle = windows.CurrentProcess()
)
err = windows.DuplicateHandle(
processHandle, // hSourceProcessHandle
windows.Handle(oldfd), // hSourceHandle
processHandle, // hTargetProcessHandle
&newfdHandle, // lpTargetHandle
0, // dwDesiredAccess
true, // bInheritHandle
windows.DUPLICATE_SAME_ACCESS, // dwOptions
)
if err != nil {
return 0, err
}
return nil
return uintptr(newfdHandle), nil
}
// redirectStderr to the file passed in
func redirectStderr(f *os.File) {
err := setStdHandle(syscall.STD_ERROR_HANDLE, syscall.Handle(f.Fd()))
termFd, err := dup(os.Stderr.Fd())
if err != nil {
log.Fatalf("Failed to duplicate stderr: %v", err)
}
terminal.RawOut = os.NewFile(termFd, "termOut")
err = windows.SetStdHandle(windows.STD_ERROR_HANDLE, windows.Handle(f.Fd()))
if err != nil {
log.Fatalf("Failed to redirect stderr to file: %v", err)
}
os.Stderr = f
}

View File

@ -81,8 +81,11 @@ func Start() {
// If output is not a tty then remove escape codes
Out = colorable.NewNonColorable(f)
} else if runtime.GOOS == "windows" && os.Getenv("TERM") != "" {
// If TERM is set just use stdout
Out = os.Stdout
// If TERM is set on Windows then we should just send output
// straight to the terminal for cygwin/git bash environments.
// We don't want to use NewColorable here because it will
// use Windows console calls which cygwin/git bash don't support.
Out = f
} else {
Out = colorable.NewColorable(f)
}