-
Notifications
You must be signed in to change notification settings - Fork 6
/
main.go
128 lines (104 loc) · 2.95 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package main
import (
"flag"
"fmt"
"os"
"path"
"strings"
)
// Version is the current value injected at build time.
var Version string = "HEAD"
// blocklist contains the list of programs not working well with an allocated pty.
var blocklist = map[string]bool{
"gio": true,
"podman": true,
"kde-open": true,
"kde-open5": true,
"xdg-open": true,
}
// Command line options
var flagPty = flag.Bool("pty", false, "Force allocate a pseudo-terminal for the host process")
var flagNoPty = flag.Bool("no-pty", false, "Do not allocate a pseudo-terminal for the host process")
var flagVersion = flag.Bool("version", false, "Show this program's version")
var flagEnvironmentVariables = flag.String("env", "TERM", "Comma separated list of environment variables to pass to the host process.")
var flagWorkingDirectory = flag.String("cwd", "", "Change working directory of the spawned process")
const OUR_BASENAME = "host-spawn"
// The exit code we return to identify an error in host-spawn itself,
// rather than in the host process
const OUR_EXIT_CODE = 127
func parseArguments() {
const USAGE_PREAMBLE = `Usage: %s [options] [ COMMAND [ arguments... ] ]
If COMMAND is not set, spawn a shell on the host.
Accepted options:
`
const USAGE_FOOTER = `--
If neither pty option is passed, default to allocating a pseudo-terminal unless
the command is known for misbehaving when attached to a pty.
For more details visit https://github.com/1player/host-spawn/issues/12
`
flag.Usage = func() {
fmt.Fprintf(os.Stderr, USAGE_PREAMBLE, os.Args[0])
flag.PrintDefaults()
fmt.Fprintf(os.Stderr, USAGE_FOOTER)
os.Exit(0)
}
flag.Parse()
if *flagVersion {
fmt.Println(Version)
os.Exit(0)
}
}
func main() {
var args []string
basename := path.Base(os.Args[0])
// Check if we're shimming a host command
if basename == OUR_BASENAME {
parseArguments()
args = flag.Args()
// If no command is given, spawn a shell
if len(args) == 0 {
args = []string{"sh", "-c", "$SHELL"}
}
} else {
args = append([]string{basename}, os.Args[1:]...)
}
// Lookup if this is a blocklisted program, where we won't enable pty.
allocatePty := !blocklist[args[0]]
if *flagPty {
allocatePty = true
} else if *flagNoPty {
allocatePty = false
}
// Get working directory
var wd string
if *flagWorkingDirectory != "" {
wd = *flagWorkingDirectory
} else {
var err error
wd, err = os.Getwd()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(OUR_EXIT_CODE)
}
}
// Lookup and passthrough environment variables
envVars := make(map[string]string)
for _, k := range strings.Split(*flagEnvironmentVariables, ",") {
if v, ok := os.LookupEnv(k); ok {
envVars[k] = v
}
}
// OK, let's go
command := Command{
Args: args,
WorkingDirectory: wd,
AllocatePty: allocatePty,
EnvVars: envVars,
}
exitCode, err := command.SpawnAndWait()
if err != nil {
fmt.Fprintln(os.Stderr, err)
exitCode = OUR_EXIT_CODE
}
os.Exit(exitCode)
}