Skip to content

Commit 4613fc6

Browse files
authored
Merge pull request #599 from bmeneguele/plat-util
util: create os-specific layer for syscalls
2 parents f5951aa + 090ff30 commit 4613fc6

File tree

3 files changed

+95
-7
lines changed

3 files changed

+95
-7
lines changed

cmd/util.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"os/exec"
99
"strconv"
1010
"strings"
11-
"syscall"
1211

1312
"github.com/pkg/errors"
1413
"github.com/spf13/cobra"
@@ -413,8 +412,8 @@ func textToMarkdown(text string) string {
413412
// to avoid some markdown rendering garbage going to other outputs that
414413
// don't support some control chars.
415414
func isOutputTerminal() bool {
416-
if !terminal.IsTerminal(int(syscall.Stdout)) ||
417-
!terminal.IsTerminal(int(syscall.Stderr)) {
415+
if !terminal.IsTerminal(sysStdout) ||
416+
!terminal.IsTerminal(sysStderr) {
418417
return false
419418
}
420419
return true
@@ -445,8 +444,8 @@ func NewPager(fs *flag.FlagSet) *Pager {
445444
Files: []*os.File{pr, os.Stdout, os.Stderr},
446445
})
447446

448-
savedStdout, _ := syscall.Dup(syscall.Stdout)
449-
_ = syscall.Dup2(int(pw.Fd()), syscall.Stdout)
447+
savedStdout, _ := dupFD(sysStdout)
448+
_ = dupFD2(int(pw.Fd()), sysStdout)
450449

451450
return &Pager{
452451
proc: proc,
@@ -456,8 +455,8 @@ func NewPager(fs *flag.FlagSet) *Pager {
456455

457456
func (pager *Pager) Close() {
458457
if pager.stdout > 0 {
459-
_ = syscall.Dup2(pager.stdout, syscall.Stdout)
460-
_ = syscall.Close(pager.stdout)
458+
_ = dupFD2(pager.stdout, sysStdout)
459+
_ = closeFD(pager.stdout)
461460
}
462461
if pager.proc != nil {
463462
pager.proc.Wait()

cmd/util_unix.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// This file contains Linux specific calls.
2+
3+
// +build !windows
4+
5+
package cmd
6+
7+
// Since we're using some system calls that are platform-specific, we need
8+
// to make sure we have a small layer of compatibility for Unix-like and
9+
// Windows operating systems. For now, this file is still valid for BSDs
10+
// (MacOS included).
11+
12+
import "syscall"
13+
14+
// We're using the Linux API as primary model, hence we can only return
15+
// the results from the default syscalls.
16+
17+
var (
18+
sysStdout = syscall.Stdout
19+
sysStderr = syscall.Stderr
20+
)
21+
22+
func closeFD(fd int) error {
23+
return syscall.Close(fd)
24+
}
25+
26+
func dupFD(fd int) (int, error) {
27+
return syscall.Dup(fd)
28+
}
29+
30+
func dupFD2(newFD, oldFD int) error {
31+
return syscall.Dup2(newFD, oldFD)
32+
}

cmd/util_windows.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// This file contains Windows specific calls.
2+
3+
// +build windows
4+
5+
package cmd
6+
7+
// Even though Windows has a POSIX layer, it's implemented in userspace and,
8+
// consequently, the "syscall" lib doesn't export it.
9+
// Because of it, we need to use specific Windows calls to handle some of the
10+
// syscall we're using the `lab`.
11+
12+
import "syscall"
13+
14+
// SetStdHandle is not exported by golang syscall lib, we need to get it
15+
// ourselves from kernel32.dll.
16+
var (
17+
kernel32 = syscall.MustLoadDLL("kernel32.dll")
18+
procSetStdHandleAddr = kernel32.MustFindProc("SetStdHandle").Addr()
19+
)
20+
21+
// Windows has the concept of "Handles", which in Unix can be directly
22+
// converted to integers.
23+
var (
24+
sysStdout = int(syscall.Stdout)
25+
sysStderr = int(syscall.Stderr)
26+
)
27+
28+
// closeFD behaves the as POSIX close()
29+
func closeFD(fd int) error {
30+
return syscall.Close(syscall.Handle(fd))
31+
}
32+
33+
// dupFD behaves the same as POSIX dup()
34+
func dupFD(fd int) (int, error) {
35+
proc, err := syscall.GetCurrentProcess()
36+
if err != nil {
37+
return 0, err
38+
}
39+
40+
var hndl syscall.Handle
41+
err = syscall.DuplicateHandle(proc, syscall.Handle(fd), proc, &hndl, 0, true, syscall.DUPLICATE_SAME_ACCESS)
42+
return int(hndl), err
43+
}
44+
45+
// dupFD2 behaves the same as POSIX dup2()
46+
func dupFD2(oldFD, newFD int) error {
47+
ret, _, err := syscall.Syscall(procSetStdHandleAddr, 2, uintptr(oldFD), uintptr(newFD), 0)
48+
if err != 0 {
49+
return error(err)
50+
}
51+
52+
if ret == 0 {
53+
return syscall.EINVAL
54+
}
55+
56+
return nil
57+
}

0 commit comments

Comments
 (0)