github.com/containerd/Containerd@v1.4.13/runtime/v2/shim/util_unix.go (about)

     1  // +build !windows
     2  
     3  /*
     4     Copyright The containerd Authors.
     5  
     6     Licensed under the Apache License, Version 2.0 (the "License");
     7     you may not use this file except in compliance with the License.
     8     You may obtain a copy of the License at
     9  
    10         http://www.apache.org/licenses/LICENSE-2.0
    11  
    12     Unless required by applicable law or agreed to in writing, software
    13     distributed under the License is distributed on an "AS IS" BASIS,
    14     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15     See the License for the specific language governing permissions and
    16     limitations under the License.
    17  */
    18  
    19  package shim
    20  
    21  import (
    22  	"context"
    23  	"crypto/sha256"
    24  	"fmt"
    25  	"net"
    26  	"os"
    27  	"path/filepath"
    28  	"strings"
    29  	"syscall"
    30  	"time"
    31  
    32  	"github.com/containerd/containerd/namespaces"
    33  	"github.com/containerd/containerd/pkg/dialer"
    34  	"github.com/containerd/containerd/sys"
    35  	"github.com/pkg/errors"
    36  )
    37  
    38  const (
    39  	shimBinaryFormat = "containerd-shim-%s-%s"
    40  	socketPathLimit  = 106
    41  )
    42  
    43  func getSysProcAttr() *syscall.SysProcAttr {
    44  	return &syscall.SysProcAttr{
    45  		Setpgid: true,
    46  	}
    47  }
    48  
    49  // SetScore sets the oom score for a process
    50  func SetScore(pid int) error {
    51  	return sys.SetOOMScore(pid, sys.OOMScoreMaxKillable)
    52  }
    53  
    54  // AdjustOOMScore sets the OOM score for the process to the parents OOM score +1
    55  // to ensure that they parent has a lower* score than the shim
    56  // if not already at the maximum OOM Score
    57  func AdjustOOMScore(pid int) error {
    58  	parent := os.Getppid()
    59  	score, err := sys.GetOOMScoreAdj(parent)
    60  	if err != nil {
    61  		return errors.Wrap(err, "get parent OOM score")
    62  	}
    63  	shimScore := score + 1
    64  	if shimScore > sys.OOMScoreAdjMax {
    65  		shimScore = sys.OOMScoreAdjMax
    66  	}
    67  	if err := sys.SetOOMScore(pid, shimScore); err != nil {
    68  		return errors.Wrap(err, "set shim OOM score")
    69  	}
    70  	return nil
    71  }
    72  
    73  const socketRoot = "/run/containerd"
    74  
    75  // SocketAddress returns a socket address
    76  func SocketAddress(ctx context.Context, socketPath, id string) (string, error) {
    77  	ns, err := namespaces.NamespaceRequired(ctx)
    78  	if err != nil {
    79  		return "", err
    80  	}
    81  	d := sha256.Sum256([]byte(filepath.Join(socketPath, ns, id)))
    82  	return fmt.Sprintf("unix://%s/%x", filepath.Join(socketRoot, "s"), d), nil
    83  }
    84  
    85  // AnonDialer returns a dialer for a socket
    86  func AnonDialer(address string, timeout time.Duration) (net.Conn, error) {
    87  	return dialer.Dialer(socket(address).path(), timeout)
    88  }
    89  
    90  func AnonReconnectDialer(address string, timeout time.Duration) (net.Conn, error) {
    91  	return AnonDialer(address, timeout)
    92  }
    93  
    94  // NewSocket returns a new socket
    95  func NewSocket(address string) (*net.UnixListener, error) {
    96  	var (
    97  		sock = socket(address)
    98  		path = sock.path()
    99  	)
   100  	if !sock.isAbstract() {
   101  		if err := os.MkdirAll(filepath.Dir(path), 0600); err != nil {
   102  			return nil, errors.Wrapf(err, "%s", path)
   103  		}
   104  	}
   105  	l, err := net.Listen("unix", path)
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  	if err := os.Chmod(path, 0600); err != nil {
   110  		os.Remove(sock.path())
   111  		l.Close()
   112  		return nil, err
   113  	}
   114  	return l.(*net.UnixListener), nil
   115  }
   116  
   117  const abstractSocketPrefix = "\x00"
   118  
   119  type socket string
   120  
   121  func (s socket) isAbstract() bool {
   122  	return !strings.HasPrefix(string(s), "unix://")
   123  }
   124  
   125  func (s socket) path() string {
   126  	path := strings.TrimPrefix(string(s), "unix://")
   127  	// if there was no trim performed, we assume an abstract socket
   128  	if len(path) == len(s) {
   129  		path = abstractSocketPrefix + path
   130  	}
   131  	return path
   132  }
   133  
   134  // RemoveSocket removes the socket at the specified address if
   135  // it exists on the filesystem
   136  func RemoveSocket(address string) error {
   137  	sock := socket(address)
   138  	if !sock.isAbstract() {
   139  		return os.Remove(sock.path())
   140  	}
   141  	return nil
   142  }
   143  
   144  // SocketEaddrinuse returns true if the provided error is caused by the
   145  // EADDRINUSE error number
   146  func SocketEaddrinuse(err error) bool {
   147  	netErr, ok := err.(*net.OpError)
   148  	if !ok {
   149  		return false
   150  	}
   151  	if netErr.Op != "listen" {
   152  		return false
   153  	}
   154  	syscallErr, ok := netErr.Err.(*os.SyscallError)
   155  	if !ok {
   156  		return false
   157  	}
   158  	errno, ok := syscallErr.Err.(syscall.Errno)
   159  	if !ok {
   160  		return false
   161  	}
   162  	return errno == syscall.EADDRINUSE
   163  }
   164  
   165  // CanConnect returns true if the socket provided at the address
   166  // is accepting new connections
   167  func CanConnect(address string) bool {
   168  	conn, err := AnonDialer(address, 100*time.Millisecond)
   169  	if err != nil {
   170  		return false
   171  	}
   172  	conn.Close()
   173  	return true
   174  }