diff --git a/.gitignore b/.gitignore index eb83fb6..173b627 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,6 @@ gradle-app.setting # Ignore Gradle build output directory build -run/ \ No newline at end of file +run/ + +serverhost-start-wrapper/run \ No newline at end of file diff --git a/serverhost-start-wrapper/Makefile b/serverhost-start-wrapper/Makefile new file mode 100644 index 0000000..d4d2a53 --- /dev/null +++ b/serverhost-start-wrapper/Makefile @@ -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" diff --git a/serverhost-start-wrapper/go.mod b/serverhost-start-wrapper/go.mod new file mode 100644 index 0000000..c1d3cd0 --- /dev/null +++ b/serverhost-start-wrapper/go.mod @@ -0,0 +1,3 @@ +module serverhost-start-wrapper + +go 1.22 diff --git a/serverhost-start-wrapper/main.go b/serverhost-start-wrapper/main.go new file mode 100644 index 0000000..8a28bd2 --- /dev/null +++ b/serverhost-start-wrapper/main.go @@ -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) + } +}