Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ gradle-app.setting
# Ignore Gradle build output directory
build

run/
run/

serverhost-start-wrapper/run
158 changes: 158 additions & 0 deletions serverhost-start-wrapper/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# Binary name
BINARY_NAME=start-wrapper

# Build directory
BUILD_DIR=build

# Version
VERSION=$(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")

# Build flags
LD_FLAGS=-ldflags "-X main.Version=$(VERSION)"

# Disable CGO for cross-compilation
CGO_ENABLED=0
export CGO_ENABLED

# Platform-specific architectures
WINDOWS_ARCH=amd64 386 arm arm64
LINUX_ARCH=amd64 386 arm arm64 ppc64 ppc64le mips mips64 mipsle mips64le riscv64 s390x
DARWIN_ARCH=amd64 arm64
FREEBSD_ARCH=amd64 386 arm arm64
OPENBSD_ARCH=amd64 386 arm arm64
NETBSD_ARCH=amd64 386 arm arm64
DRAGONFLY_ARCH=amd64
SOLARIS_ARCH=amd64
AIX_ARCH=ppc64
ILLUMOS_ARCH=amd64
PLAN9_ARCH=amd64 386 arm

# Default task
.PHONY: all
all: clean build-all

# Clean build directory
.PHONY: clean
clean:
rm -rf $(BUILD_DIR)

# Create build directory
.PHONY: init
init:
mkdir -p $(BUILD_DIR)

# Build for all platforms
.PHONY: build-all
build-all: init windows linux darwin bsd special

# Windows builds
.PHONY: windows
windows:
@echo "Building for Windows..."
@for arch in $(WINDOWS_ARCH); do \
echo "Building for windows/$$arch..."; \
GOOS=windows GOARCH=$$arch go build $(LD_FLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-windows-$$arch.exe .; \
done

# Linux builds
.PHONY: linux
linux:
@echo "Building for Linux..."
@for arch in $(LINUX_ARCH); do \
echo "Building for linux/$$arch..."; \
GOOS=linux GOARCH=$$arch go build $(LD_FLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-$$arch .; \
done

# Darwin (macOS) builds
.PHONY: darwin
darwin:
@echo "Building for macOS..."
@for arch in $(DARWIN_ARCH); do \
echo "Building for darwin/$$arch..."; \
GOOS=darwin GOARCH=$$arch go build $(LD_FLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-darwin-$$arch .; \
done

# BSD builds
.PHONY: bsd
bsd:
@echo "Building for FreeBSD..."
@for arch in $(FREEBSD_ARCH); do \
echo "Building for freebsd/$$arch..."; \
GOOS=freebsd GOARCH=$$arch go build $(LD_FLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-freebsd-$$arch .; \
done
@echo "Building for OpenBSD..."
@for arch in $(OPENBSD_ARCH); do \
echo "Building for openbsd/$$arch..."; \
GOOS=openbsd GOARCH=$$arch go build $(LD_FLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-openbsd-$$arch .; \
done
@echo "Building for NetBSD..."
@for arch in $(NETBSD_ARCH); do \
echo "Building for netbsd/$$arch..."; \
GOOS=netbsd GOARCH=$$arch go build $(LD_FLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-netbsd-$$arch .; \
done
@echo "Building for DragonFly BSD..."
@for arch in $(DRAGONFLY_ARCH); do \
echo "Building for dragonfly/$$arch..."; \
GOOS=dragonfly GOARCH=$$arch go build $(LD_FLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-dragonfly-$$arch .; \
done

# Special platform builds
.PHONY: special
special:
@echo "Building for special platforms..."
@echo "Building for Solaris..."
@for arch in $(SOLARIS_ARCH); do \
GOOS=solaris GOARCH=$$arch go build $(LD_FLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-solaris-$$arch; \
done
@echo "Building for AIX..."
@for arch in $(AIX_ARCH); do \
GOOS=aix GOARCH=$$arch go build $(LD_FLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-aix-$$arch; \
done
@echo "Building for IllumOS..."
@for arch in $(ILLUMOS_ARCH); do \
GOOS=illumos GOARCH=$$arch go build $(LD_FLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-illumos-$$arch; \
done
@echo "Building for Plan9..."
@for arch in $(PLAN9_ARCH); do \
GOOS=plan9 GOARCH=$$arch go build $(LD_FLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-plan9-$$arch; \
done

# Create release archives
.PHONY: release
release: build-all
@echo "Creating release archives..."
@cd $(BUILD_DIR) && \
for file in $(BINARY_NAME)-*; do \
if [[ $$file == *.exe ]]; then \
zip "$$file.zip" "$$file"; \
else \
tar czf "$$file.tar.gz" "$$file"; \
fi \
done

# Build for current platform (for development)
.PHONY: build
build:
go build $(LD_FLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)

# Run tests
.PHONY: test
test:
go test -v ./...

# Show available targets
.PHONY: help
help:
@echo "Available targets:"
@echo " all - Clean and build for all platforms"
@echo " clean - Remove build directory"
@echo " build - Build for current platform"
@echo " release - Create release archives"
@echo " test - Run tests"
@echo ""
@echo "Platform-specific builds:"
@echo " windows - Build Windows binaries"
@echo " linux - Build Linux binaries"
@echo " darwin - Build macOS binaries"
@echo " bsd - Build BSD binaries"
@echo " special - Build for other platforms"
3 changes: 3 additions & 0 deletions serverhost-start-wrapper/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module serverhost-start-wrapper

go 1.22
114 changes: 114 additions & 0 deletions serverhost-start-wrapper/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package main

import (
"bufio"
"flag"
"fmt"
"log"
"os"
"os/exec"
"strings"
)

type Hooks struct {
OnCommand func(command string)
OnOutput func(line string)
OnStart func(pid int)
OnStop func(exitCode int)
}

func RunWithHooks(command string, hooks Hooks) error {
cmdParts := strings.Fields(command)
if len(cmdParts) == 0 {
return fmt.Errorf("empty command")
}

cmd := exec.Command(cmdParts[0], cmdParts[1:]...)

stdout, err := cmd.StdoutPipe()
if err != nil {
return err
}

stdin, err := cmd.StdinPipe()
if err != nil {
return err
}

cmd.Stderr = cmd.Stdout
cmd.Env = os.Environ()

go func() {
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
line := scanner.Text()
if hooks.OnOutput != nil {
hooks.OnOutput(line)
}

fmt.Println(line)
}
}()

go func() {
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
command := scanner.Text()
if hooks.OnCommand != nil {
hooks.OnCommand(command)
}
stdin.Write([]byte(command + "\n"))
}
}()

if err := cmd.Start(); err != nil {
return err
}

if hooks.OnStart != nil {
hooks.OnStart(cmd.Process.Pid)
}

if err := cmd.Wait(); err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
if hooks.OnStop != nil {
hooks.OnStop(exitErr.ExitCode())
}
}
return err
}

if hooks.OnStop != nil {
hooks.OnStop(0)
}

return nil
}

func main() {
var command string
flag.StringVar(&command, "command", "", "The command to execute")
flag.Parse()

if command == "" {
log.Fatal("No command specified")
}

hooks := Hooks{
OnCommand: func(command string) {
log.Printf("Command sent: %s", command)
},
OnOutput: func(line string) {
},
OnStart: func(pid int) {
log.Printf("Process started with PID: %d", pid)
},
OnStop: func(exitCode int) {
log.Printf("Process stopped with exit code: %d", exitCode)
},
}

if err := RunWithHooks(command, hooks); err != nil {
log.Printf("Process error: %v", err)
}
}