Skip to content

Commit 6bc00ad

Browse files
authored
Directly attach stdin/out/err by default (#57)
1 parent 1336cb2 commit 6bc00ad

File tree

4 files changed

+80
-49
lines changed

4 files changed

+80
-49
lines changed

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
release:
1111
uses: itzg/github-workflows/.github/workflows/go-with-releaser-image.yml@main
1212
with:
13-
go-version: "1.20.10"
13+
go-version: "1.21.6"
1414
secrets:
1515
image-registry-username: ${{ secrets.DOCKERHUB_USERNAME }}
1616
image-registry-password: ${{ secrets.DOCKERHUB_TOKEN }}

.goreleaser.yml

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,12 @@ builds:
1010
- goos:
1111
- linux
1212
goarch:
13-
- 386
1413
- amd64
1514
- arm
1615
- arm64
1716
goarm:
18-
- 6
19-
- 7
17+
- "6"
18+
- "7"
2019
main: .
2120
env:
2221
- CGO_ENABLED=0
@@ -25,8 +24,6 @@ archives:
2524
- format_overrides:
2625
- goos: windows
2726
format: zip
28-
snapshot:
29-
name_template: SNAPSHOT-{{ .Commit }}
3027
changelog:
3128
filters:
3229
exclude:

main.go

Lines changed: 75 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package main
22

33
import (
44
"context"
5+
"errors"
56
"flag"
67
"fmt"
78
"io"
@@ -67,14 +68,43 @@ func main() {
6768
logger.Error("Unable to get stdin", zap.Error(err))
6869
}
6970

70-
stdout, err := cmd.StdoutPipe()
71-
if err != nil {
72-
logger.Error("Unable to get stdout", zap.Error(err))
73-
}
71+
if args.RemoteConsole {
72+
stdout, err := cmd.StdoutPipe()
73+
if err != nil {
74+
logger.Error("Unable to get stdout", zap.Error(err))
75+
}
7476

75-
stderr, err := cmd.StderrPipe()
76-
if err != nil {
77-
logger.Error("Unable to get stderr", zap.Error(err))
77+
stderr, err := cmd.StderrPipe()
78+
if err != nil {
79+
logger.Error("Unable to get stderr", zap.Error(err))
80+
}
81+
82+
console := makeConsole(stdin, stdout, stderr)
83+
84+
// Relay stdin between outside and server
85+
if !args.DetachStdin {
86+
go consoleInRoutine(os.Stdin, console, logger)
87+
}
88+
89+
go consoleOutRoutine(os.Stdout, console, stdOutTarget, logger)
90+
go consoleOutRoutine(os.Stderr, console, stdErrTarget, logger)
91+
92+
go runRemoteShellServer(console, logger)
93+
94+
logger.Info("Running with remote console support")
95+
} else {
96+
logger.Debug("Directly assigning stdout/stderr")
97+
// directly assign stdout/err to pass through terminal, if applicable
98+
cmd.Stdout = os.Stdout
99+
cmd.Stderr = os.Stderr
100+
101+
if hasRconCli() {
102+
logger.Debug("Directly assigning stdin")
103+
cmd.Stdin = os.Stdin
104+
stdin = os.Stdin
105+
} else {
106+
go relayStdin(logger, stdin)
107+
}
78108
}
79109

80110
err = cmd.Start()
@@ -93,26 +123,11 @@ func main() {
93123
}
94124
}
95125

96-
console := makeConsole(stdin, stdout, stderr)
97-
98-
// Relay stdin between outside and server
99-
if !args.DetachStdin {
100-
go consoleInRoutine(os.Stdin, console, logger)
101-
}
102-
103-
go consoleOutRoutine(os.Stdout, console, stdOutTarget, logger)
104-
go consoleOutRoutine(os.Stderr, console, stdErrTarget, logger)
105-
106-
// Start the remote server if intended
107-
if args.RemoteConsole {
108-
go startRemoteShellServer(console, logger)
109-
}
110-
111126
ctx, cancel := context.WithCancel(context.Background())
112-
errors := make(chan error, 1)
127+
errorChan := make(chan error, 1)
113128

114129
if args.NamedPipe != "" {
115-
err2 := handleNamedPipe(ctx, args.NamedPipe, stdin, errors)
130+
err2 := handleNamedPipe(ctx, args.NamedPipe, stdin, errorChan)
116131
if err2 != nil {
117132
logger.Fatal("Failed to setup named pipe", zap.Error(err2))
118133
}
@@ -123,14 +138,12 @@ func main() {
123138
go func() {
124139
waitErr := cmd.Wait()
125140
if waitErr != nil {
126-
if exitErr, ok := waitErr.(*exec.ExitError); ok {
141+
var exitErr *exec.ExitError
142+
if errors.As(waitErr, &exitErr) {
127143
exitCode := exitErr.ExitCode()
128144
logger.Warn("Minecraft server failed. Inspect logs above for errors that indicate cause. DO NOT report this line as an error.",
129145
zap.Int("exitCode", exitCode))
130146
cmdExitChan <- exitCode
131-
} else {
132-
logger.Error("Command failed abnormally", zap.Error(waitErr))
133-
cmdExitChan <- 1
134147
}
135148
return
136149
} else {
@@ -142,7 +155,7 @@ func main() {
142155
select {
143156
case <-signalChan:
144157
if args.StopServerAnnounceDelay > 0 {
145-
announceStopViaConsole(logger, stdin, args.StopServerAnnounceDelay)
158+
announceStop(logger, stdin, args.StopServerAnnounceDelay)
146159
logger.Info("Sleeping before server stop", zap.Duration("sleepTime", args.StopServerAnnounceDelay))
147160
time.Sleep(args.StopServerAnnounceDelay)
148161
}
@@ -168,7 +181,7 @@ func main() {
168181
})
169182
}
170183

171-
case namedPipeErr := <-errors:
184+
case namedPipeErr := <-errorChan:
172185
logger.Error("Error during named pipe handling", zap.Error(namedPipeErr))
173186

174187
case exitCode := <-cmdExitChan:
@@ -180,6 +193,13 @@ func main() {
180193

181194
}
182195

196+
func relayStdin(logger *zap.Logger, stdin io.WriteCloser) {
197+
_, err := io.Copy(stdin, os.Stdin)
198+
if err != nil {
199+
logger.Error("Failed to relay standard input", zap.Error(err))
200+
}
201+
}
202+
183203
func hasRconCli() bool {
184204
if strings.ToUpper(os.Getenv("ENABLE_RCON")) == "TRUE" {
185205
_, err := exec.LookPath("rcon-cli")
@@ -189,9 +209,7 @@ func hasRconCli() bool {
189209
}
190210
}
191211

192-
func stopWithRconCli() error {
193-
log.Println("Stopping with rcon-cli")
194-
212+
func sendRconCommand(cmd ...string) error {
195213
rconConfigFile := os.Getenv("RCON_CONFIG_FILE")
196214
if rconConfigFile == "" {
197215
port := os.Getenv("RCON_PORT")
@@ -204,23 +222,39 @@ func stopWithRconCli() error {
204222
password = "minecraft"
205223
}
206224

207-
rconCliCmd := exec.Command("rcon-cli",
208-
"--port", port,
209-
"--password", password,
210-
"stop")
225+
args := []string{"--port", port,
226+
"--password", password}
227+
args = append(args, cmd...)
228+
229+
rconCliCmd := exec.Command("rcon-cli", args...)
211230

212231
return rconCliCmd.Run()
213232
} else {
214-
rconCliCmd := exec.Command("rcon-cli",
215-
"--config", rconConfigFile,
216-
"stop")
233+
234+
args := []string{"--config", rconConfigFile}
235+
args = append(args, cmd...)
236+
237+
rconCliCmd := exec.Command("rcon-cli", args...)
217238

218239
return rconCliCmd.Run()
219240
}
220241
}
221242

222-
func announceStopViaConsole(logger *zap.Logger, stdin io.Writer, shutdownDelay time.Duration) {
243+
func stopWithRconCli() error {
244+
log.Println("Stopping with rcon-cli")
245+
246+
return sendRconCommand("stop")
247+
}
248+
249+
func announceStop(logger *zap.Logger, stdin io.Writer, shutdownDelay time.Duration) {
223250
logger.Info("Sending shutdown announce 'say' to Minecraft server")
251+
if hasRconCli() {
252+
err := sendRconCommand("say", fmt.Sprintf("Server shutting down in %0.f seconds", shutdownDelay.Seconds()))
253+
if err != nil {
254+
logger.Error("Failed to send 'say' command", zap.Error(err))
255+
}
256+
}
257+
224258
_, err := stdin.Write([]byte(fmt.Sprintf("say Server shutting down in %0.f seconds\n", shutdownDelay.Seconds())))
225259
if err != nil {
226260
logger.Error("Failed to write say command to server console", zap.Error(err))

remote_shell_service.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ func consoleOutRoutine(output io.Writer, console *Console, target ConsoleTarget,
183183

184184
// Use os.Stdin for console.
185185
func consoleInRoutine(stdIn io.Reader, console *Console, logger *zap.Logger) {
186-
scanner := bufio.NewScanner(os.Stdin)
186+
scanner := bufio.NewScanner(stdIn)
187187
for scanner.Scan() {
188188
text := scanner.Text()
189189
outBytes := []byte(fmt.Sprintf("%s\n", text))
@@ -229,7 +229,7 @@ func ensureHostKey(logger *zap.Logger) (string, error) {
229229
return keyfilePath, err
230230
}
231231

232-
func startRemoteShellServer(console *Console, logger *zap.Logger) {
232+
func runRemoteShellServer(console *Console, logger *zap.Logger) {
233233
logger.Info("Starting remote shell server on 2222...")
234234
ssh.Handle(func(s ssh.Session) { handleSession(s, console, logger) })
235235

0 commit comments

Comments
 (0)