...

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

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

     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 activatecmd
    15  
    16  import (
    17  	"fmt"
    18  	"net"
    19  	"os"
    20  	"os/exec"
    21  	"strings"
    22  	"syscall"
    23  )
    24  
    25  var prefixes = []string{"NOTIFY_", "LISTEN_", "EINHORN_"}
    26  
    27  // ClearEnv removes all items from an environment list that might be interpreted
    28  // as a notification socket or inherited fd
    29  func ClearEnv(env []string) (ret []string) {
    30  	for _, e := range env {
    31  		ok := true
    32  		for _, prefix := range prefixes {
    33  			if strings.HasPrefix(e, prefix) {
    34  				ok = false
    35  				break
    36  			}
    37  		}
    38  		if ok {
    39  			ret = append(ret, e)
    40  		}
    41  	}
    42  	return
    43  }
    44  
    45  type ListenerSet struct {
    46  	files []*os.File
    47  }
    48  
    49  // NewListenerSet prepares a set of listeners that can be attached to child processes.
    50  //
    51  // The underyling files are duplicated, so the original Listener objects can be
    52  // closed if desired.
    53  func NewListenerSet(listeners []net.Listener) (*ListenerSet, error) {
    54  	s := new(ListenerSet)
    55  	for i, lis := range listeners {
    56  		lf, ok := lis.(filer)
    57  		if !ok {
    58  			return nil, fmt.Errorf("unable to get file from listener %d (type %T)", i, lis)
    59  		}
    60  		f, err := lf.File()
    61  		if err != nil {
    62  			return nil, fmt.Errorf("unable to get file from listener %d: %s", i, err)
    63  		}
    64  		// File() puts the file description into blocking mode. Put it back and
    65  		// leave it that way, otherwise it will race with child processes
    66  		// trying to accept from it.
    67  		if err := syscall.SetNonblock(int(f.Fd()), true); err != nil {
    68  			return nil, fmt.Errorf("unable to get file from listener %d: %s", i, err)
    69  		}
    70  		s.files = append(s.files, f)
    71  	}
    72  	return s, nil
    73  }
    74  
    75  // Close frees the extra file descriptors owned by the listener set
    76  func (s *ListenerSet) Close() error {
    77  	for _, f := range s.files {
    78  		f.Close()
    79  	}
    80  	return nil
    81  }
    82  
    83  // Attach all the listeners in the set to a new child process.
    84  func (s *ListenerSet) Attach(cmd *exec.Cmd) error {
    85  	if cmd.Env == nil {
    86  		cmd.Env = ClearEnv(os.Environ())
    87  	}
    88  	for i, f := range s.files {
    89  		cmd.Env = append(cmd.Env, fmt.Sprintf("EINHORN_FD_%d=%d", i, 3+len(cmd.ExtraFiles)))
    90  		cmd.ExtraFiles = append(cmd.ExtraFiles, f)
    91  	}
    92  	cmd.Env = append(cmd.Env, fmt.Sprintf("EINHORN_FD_COUNT=%d", len(s.files)))
    93  	cmd.Env = append(cmd.Env, fmt.Sprintf("EINHORN_MASTER_PID=%d", os.Getpid()))
    94  	return nil
    95  }
    96  
    97  type filer interface {
    98  	File() (*os.File, error)
    99  }
   100  

View as plain text