github.com/bored-engineer/go-launchd@v0.0.0-20230905041514-6aff1185c30f/README.md (about) 1 # go-launchd [![Go Reference](https://pkg.go.dev/badge/github.com/bored-engineer/go-launchd.svg)](https://pkg.go.dev/github.com/bored-engineer/go-launchd) [![Test Workflow](https://github.com/bored-engineer/go-launchd/actions/workflows/test.yml/badge.svg)](https://github.com/bored-engineer/go-launchd/actions/workflows/test.yml) 2 [Golang](https://go.dev/) support for macOS (Darwin) launchd socket activation ([launch_activate_socket](https://developer.apple.com/documentation/xpc/1505523-launch_activate_socket)) without [cgo](https://pkg.go.dev/cmd/cgo). 3 4 ## How it works 5 Using a similar technique as the [crypto/x509 package](https://go-review.googlesource.com/c/go/+/232397) and [golang.org/x/sys/unix package](https://pkg.go.dev/golang.org/x/sys/unix) it is possible to import the [launch_activate_socket](https://developer.apple.com/documentation/xpc/1505523-launch_activate_socket) function from `libxpc.dylib` at runtime via the `go:cgo_import_dynamic` directive: 6 ```go 7 // launch_activate_socket is defined in libxpc.dylib 8 var libxpc_launch_activate_socket_trampoline_addr uintptr 9 10 //go:cgo_import_dynamic libxpc_launch_activate_socket launch_activate_socket "/usr/lib/system/libxpc.dylib" 11 ``` 12 This must be combined with a golang assembly file ([libxpc.s](./libxpc.s)) to populate the "trampoline" variable with a pointer to the relevant function: 13 ``` 14 TEXT libxpc_launch_activate_socket_trampoline<>(SB),NOSPLIT,$0-0 15 JMP libxpc_launch_activate_socket(SB) 16 17 GLOBL ·libxpc_launch_activate_socket_trampoline_addr(SB), RODATA, $8 18 DATA ·libxpc_launch_activate_socket_trampoline_addr(SB)/8, $libxpc_launch_activate_socket_trampoline<>(SB) 19 ``` 20 Finally by linking the internal `syscall.syscall` function we are able to invoke the [launch_activate_socket](https://developer.apple.com/documentation/xpc/1505523-launch_activate_socket) function: 21 ```go 22 // Implemented in the runtime package (runtime/sys_darwin.go) 23 func syscall_syscall(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) 24 25 //go:linkname syscall_syscall syscall.syscall 26 ``` 27 The remaining logic is primarily implemented in [libxpc.go](./libxpc.go) to convert from C structures into Golang equivalents. 28 29 ## Usage 30 See also the [example/](./example/) directory for the associated launchd job definition (plist): 31 ```go 32 package main 33 34 import launchd "github.com/bored-engineer/go-launchd" 35 36 func main() { 37 l, err := launchd.Activate("Listeners") 38 if err != nil { 39 log.Fatalf("launchd.Socket failed: %s", err) 40 } 41 for { 42 conn, err := l.Accept() 43 if err != nil { 44 log.Printf("(net.Listener).Accept failed: %s", err) 45 continue 46 } 47 go func(conn net.Conn) { 48 defer conn.Close() 49 io.Copy(conn, conn) 50 }(conn) 51 } 52 } 53 54 ```