launchd

package module
v0.0.0-...-16c17c9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 10, 2024 License: MIT Imports: 4 Imported by: 0

README

🚀 go-launchd

go-reference go-version test lint release license version

Socket Activation

Usage

See API docs for more info and examples.

See Also

For systemd socket activation, Use github.com/tprasadtp/go-systemd.

Testing

Testing requires macOS and go version 1.21 or later.

go test -cover ./...

Documentation

Overview

Package launchd provides pure go bindings for macOS launchd.

Supports launch_activate_socket without using cgo.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Files

func Files(name string) ([]*os.File, error)

Files returns slice of *os.File backed by file descriptors for given socket.

This must be called exactly once for given socket name. Subsequent calls with the same socket name will return syscall.EALREADY.

func Listeners

func Listeners(name string) ([]net.Listener, error)

Listeners returns slice of net.Listener for specified TCP/stream socket.

In case of error building listeners, an appropriate error is returned, along with a partial list of listeners. It is the responsibility of the caller to close the returned non-nil listeners whenever required.

Closing returned listeners does not close underlying file descriptor and closing files does not affect the listeners.

This must be called exactly once for a given socket name. Subsequent calls with the same socket name will return syscall.EALREADY.

Example
// SPDX-FileCopyrightText: Copyright 2023 Prasad Tengse
// SPDX-License-Identifier: MIT

package main

import (
	"context"
	"errors"
	"log/slog"
	"net"
	"net/http"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"

	"github.com/tprasadtp/go-launchd"
)

// WaitGroup to wait on multiple listeners.
var wg sync.WaitGroup

func main() {
	// This example only works on macOS, But is shown on all platforms
	// for ease of use. This cannot be used for systemd socket activation.
	listeners, err := launchd.Listeners("socket-name-as-in-plist")
	if err != nil {
		slog.Error("Error getting socket activated listeners", "err", err)
		// Handle error and close any active listeners.
		for _, item := range listeners {
			item.Close()
		}
		os.Exit(1)
	}

	// A simple HTTP handler. Replace this with your actual implementation.
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		_, _ = w.Write([]byte("Hello From Socket Activated Server\n"))
		slog.Info("Request received",
			"client", r.RemoteAddr,
			"method", r.Method,
			"url", r.URL)
	})

	// Make servers stoppable with ctrl+x or SIGTERM.
	ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
	defer cancel()

	// Because there may be multiple listeners, we need to have as many servers as listeners.
	servers := make([]*http.Server, 0, len(listeners))
	for range listeners {
		servers = append(servers, &http.Server{
			Handler:           handler,
			ReadHeaderTimeout: time.Second * 30,
		})
	}

	for i := range listeners {
		// Wait for context to cancel and stop server.
		wg.Add(1)
		go func(s *http.Server, l net.Listener) {
			defer wg.Done()
			var err error
			for {
				select {
				case <-ctx.Done():
					slog.Info("Stopping server", "address", l.Addr())
					// In production do this with a timeout.
					err = s.Shutdown(context.Background())
					if err != nil && !errors.Is(err, http.ErrServerClosed) {
						slog.Error("Failed to shutdown server",
							"err", err, "address", l.Addr())
					}
					return
				}
			}
		}(servers[i], listeners[i])

		// Run servers in background if context is not already cancelled.
		if ctx.Err() == nil {
			wg.Add(1)
			go func(s *http.Server, l net.Listener) {
				defer wg.Done()
				slog.Info("Starting server", "address", l.Addr())
				if err := s.Serve(l); !errors.Is(err, http.ErrServerClosed) {
					slog.Error("Error", "address", l.Addr(), "err", err)
					cancel()
				}
			}(servers[i], listeners[i])
		}
	}

	// Wait for all servers to exit.
	wg.Wait()
	slog.Info("Server(s) stopped")
}

func PacketListeners

func PacketListeners(name string) ([]net.PacketConn, error)

PacketListeners returns slice of net.PacketConn for specified UDP/datagram socket.

In case of error building net.PacketConn, an appropriate error is returned, along with a partial list of net.PacketConn. It is the responsibility of the caller to close the returned non-nil listeners whenever required.

Closing returned listeners does not close underlying file descriptor and closing files does not affect the listeners.

This must be called exactly once for a given socket name. Subsequent calls with the same socket name will return syscall.EALREADY.

func TCPListeners deprecated

func TCPListeners(name string) ([]net.Listener, error)

Deprecated: Use Listeners.

func UDPListeners deprecated

func UDPListeners(name string) ([]net.PacketConn, error)

Deprecated: Use PacketListeners.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL