/usr/share/gocode/src/blitiri.com.ar/go/systemd/systemd.go is in golang-blitiri-go-systemd-dev 0.0+git20170821.0.aec3508-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | // Package systemd implements a way to get network listeners from systemd,
// similar to C's sd_listen_fds(3) and sd_listen_fds_with_names(3).
package systemd // import "blitiri.com.ar/go/systemd"
import (
"errors"
"fmt"
"net"
"os"
"strconv"
"strings"
"sync"
"syscall"
)
var (
// Error to return when $LISTEN_PID does not refer to us.
ErrPIDMismatch = errors.New("$LISTEN_PID != our PID")
// First FD for listeners.
// It's 3 by definition, but using a variable simplifies testing.
firstFD = 3
)
// Keep a single global map of listeners, to avoid repeatedly parsing which
// can be problematic (see parse).
var listeners map[string][]net.Listener
var parseError error
var mutex sync.Mutex
// parse listeners, updating the global state.
// This function messes with file descriptors and environment, so it is not
// idempotent and must be called only once. For the callers' convenience, we
// save the listeners map globally and reuse it on the user-visible functions.
func parse() {
mutex.Lock()
defer mutex.Unlock()
if listeners != nil {
return
}
pidStr := os.Getenv("LISTEN_PID")
nfdsStr := os.Getenv("LISTEN_FDS")
fdNamesStr := os.Getenv("LISTEN_FDNAMES")
fdNames := strings.Split(fdNamesStr, ":")
// Nothing to do if the variables are not set.
if pidStr == "" || nfdsStr == "" {
return
}
pid, err := strconv.Atoi(pidStr)
if err != nil {
parseError = fmt.Errorf(
"error converting $LISTEN_PID=%q: %v", pidStr, err)
return
} else if pid != os.Getpid() {
parseError = ErrPIDMismatch
return
}
nfds, err := strconv.Atoi(os.Getenv("LISTEN_FDS"))
if err != nil {
parseError = fmt.Errorf(
"error reading $LISTEN_FDS=%q: %v", nfdsStr, err)
return
}
// We should have as many names as we have descriptors.
// Note that if we have no descriptors, fdNames will be [""] (due to how
// strings.Split works), so we consider that special case.
if nfds > 0 && (fdNamesStr == "" || len(fdNames) != nfds) {
parseError = fmt.Errorf(
"Incorrect LISTEN_FDNAMES, have you set FileDescriptorName?")
return
}
listeners = map[string][]net.Listener{}
for i := 0; i < nfds; i++ {
fd := firstFD + i
// We don't want childs to inherit these file descriptors.
syscall.CloseOnExec(fd)
name := fdNames[i]
sysName := fmt.Sprintf("[systemd-fd-%d-%v]", fd, name)
lis, err := net.FileListener(os.NewFile(uintptr(fd), sysName))
if err != nil {
parseError = fmt.Errorf(
"Error making listener out of fd %d: %v", fd, err)
return
}
listeners[name] = append(listeners[name], lis)
}
// Remove them from the environment, to prevent accidental reuse (by
// us or children processes).
os.Unsetenv("LISTEN_PID")
os.Unsetenv("LISTEN_FDS")
os.Unsetenv("LISTEN_FDNAMES")
}
// Listeners returns a map of listeners for the file descriptors passed by
// systemd via environment variables.
//
// It returns a map of the form (file descriptor name -> slice of listeners).
//
// The file descriptor name comes from the "FileDescriptorName=" option in the
// systemd socket unit. Multiple socket units can have the same name, hence
// the slice of listeners for each name.
//
// Ideally you should not need to call this more than once. If you do, the
// same listeners will be returned, as repeated calls to this function will
// return the same results: the parsing is done only once, and the results are
// saved and reused.
//
// See sd_listen_fds(3) and sd_listen_fds_with_names(3) for more details on
// how the passing works.
func Listeners() (map[string][]net.Listener, error) {
parse()
return listeners, parseError
}
// OneListener returns a listener for the first systemd socket with the given
// name. If there are none, the listener and error will both be nil. An error
// will be returned only if there were issues parsing the file descriptors.
//
// This function can be convenient for simple callers where you know there's
// only one file descriptor being passed with the given name.
//
// This is a convenience function built on top of Listeners().
func OneListener(name string) (net.Listener, error) {
parse()
if parseError != nil {
return nil, parseError
}
lis := listeners[name]
if len(lis) < 1 {
return nil, nil
}
return lis[0], nil
}
// Listen returns a listener for the given address, similar to net.Listen.
//
// If the address begins with "&" it is interpreted as a systemd socket being
// passed. For example, using "&http" would mean we expect a systemd socket
// passed to us, named with "FileDescriptorName=http" in its unit.
//
// Otherwise, it uses net.Listen to create a new listener with the given net
// and local address.
//
// This function can be convenient for simple callers where you get the
// address from a user, and want to let them specify either "use systemd" or a
// normal address without too much additional complexity.
//
// This is a convenience function built on top of Listeners().
func Listen(netw, laddr string) (net.Listener, error) {
if strings.HasPrefix(laddr, "&") {
name := laddr[1:]
lis, err := OneListener(name)
if lis == nil && err == nil {
err = fmt.Errorf("systemd socket %q not found", name)
}
return lis, err
} else {
return net.Listen(netw, laddr)
}
}
|