...

Source file src/github.com/sassoftware/relic/internal/activation/listener.go

Documentation: github.com/sassoftware/relic/internal/activation

     1  // Copyright © SAS Institute Inc.
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file except in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  //     http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  // Package activation provides utilities for inheriting listening sockets from
    15  // systemd, einhorn, socketmaster, and crank.
    16  package activation
    17  
    18  import (
    19  	"errors"
    20  	"net"
    21  	"os"
    22  	"strconv"
    23  	"strings"
    24  	"syscall"
    25  )
    26  
    27  // GetListener checks if a daemon manager has passed a pre-activated listener
    28  // socket. If not, then net.Listener is used to open a new one. index starts at
    29  // 0 and increments for each additional socket being inherited.
    30  func GetListener(index uint, family, laddr string) (listener net.Listener, err error) {
    31  	listener, err = einhornListener(index)
    32  	if listener != nil || err != nil {
    33  		return
    34  	}
    35  	listener, err = socketmasterListener(index)
    36  	if listener != nil || err != nil {
    37  		return
    38  	}
    39  	listener, err = systemdListener(index)
    40  	if listener != nil || err != nil {
    41  		return
    42  	}
    43  	if family == "unix" || family == "unixpacket" {
    44  		os.Remove(laddr)
    45  	}
    46  	return net.Listen(family, laddr)
    47  }
    48  
    49  // Env vars are unset as they are read to avoid passing them to child
    50  // processes, but keep their values locally in case more than one socket is
    51  // being inherited
    52  var savedEnv map[string]string
    53  
    54  func popEnv(name string) string {
    55  	if savedEnv == nil {
    56  		savedEnv = make(map[string]string, 1)
    57  	}
    58  	val := savedEnv[name]
    59  	if val != "" {
    60  		return val
    61  	}
    62  	val = os.Getenv(name)
    63  	savedEnv[name] = val
    64  	os.Unsetenv(name)
    65  	return val
    66  }
    67  
    68  func popEnvInt(name string) (int, error) {
    69  	str := popEnv(name)
    70  	if str == "" {
    71  		return -1, nil
    72  	}
    73  	return strconv.Atoi(str)
    74  }
    75  
    76  func fdListener(fd uintptr) (net.Listener, error) {
    77  	if err := syscall.SetNonblock(int(fd), true); err != nil {
    78  		return nil, err
    79  	}
    80  	file := os.NewFile(fd, "FD_"+strconv.Itoa(int(fd)))
    81  	// FileListener dupes the fd so make sure the originally inherited one gets closed
    82  	defer file.Close()
    83  	return net.FileListener(file)
    84  }
    85  
    86  func systemdListener(index uint) (net.Listener, error) {
    87  	// systemd's socket activation places all fds sequentially starting at 3.
    88  	// It also sets LISTEN_PID to this process' PID as a safety check. Other
    89  	// runners may not set LISTEN_PID so don't worry if it's not set.
    90  	pid, err := popEnvInt("LISTEN_PID")
    91  	if err != nil || (pid != -1 && pid != os.Getpid()) {
    92  		// This FD is not for us
    93  		return nil, err
    94  	}
    95  	nfds, err := popEnvInt("LISTEN_FDS")
    96  	if err != nil || nfds < int(index)+1 {
    97  		return nil, err
    98  	}
    99  	return fdListener(uintptr(3 + index))
   100  }
   101  
   102  func einhornListener(index uint) (net.Listener, error) {
   103  	// github.com/stripe/einhorn
   104  	// Verify the parent PID as a safety check. Each fd is passed in its own
   105  	// environment variable.
   106  	ppid, err := popEnvInt("EINHORN_MASTER_PID")
   107  	if err != nil || (ppid != -1 && ppid != os.Getppid()) {
   108  		// This FD is not for us
   109  		return nil, err
   110  	}
   111  	numfds, err := popEnvInt("EINHORN_FD_COUNT")
   112  	if err != nil || numfds < int(index)+1 {
   113  		return nil, err
   114  	}
   115  	name := "EINHORN_FD_" + strconv.Itoa(int(index))
   116  	fd, err := popEnvInt(name)
   117  	if err != nil {
   118  		return nil, err
   119  	} else if fd < 0 {
   120  		return nil, errors.New("Missing environment variable " + name)
   121  	}
   122  	// Make sure the old-style var is not inherited by anybody
   123  	os.Unsetenv("EINHORN_FDS")
   124  	return fdListener(uintptr(fd))
   125  }
   126  
   127  func socketmasterListener(index uint) (net.Listener, error) {
   128  	// github.com/zimbatm/socketmaster
   129  	// Old style of einhorn fd passing, which socketmaster emulates. Uses a
   130  	// single environment variable with a space-separated list of fds.
   131  	fdstr := popEnv("EINHORN_FDS")
   132  	if fdstr == "" {
   133  		return nil, nil
   134  	}
   135  	fds := strings.Split(fdstr, " ")
   136  	if len(fds) < int(index)+1 {
   137  		return nil, nil
   138  	}
   139  	fd, err := strconv.Atoi(fds[index])
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  	return fdListener(uintptr(fd))
   144  }
   145  
   146  type filer interface {
   147  	File() (*os.File, error)
   148  }
   149  

View as plain text