github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/x/os/limits_linux.go (about)

     1  // Copyright (c) 2018 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package xos
    22  
    23  import (
    24  	"fmt"
    25  	"io/ioutil"
    26  	"strconv"
    27  	"strings"
    28  	"syscall"
    29  )
    30  
    31  const (
    32  	sysctlDir        = "/proc/sys/"
    33  	vmMaxMapCountKey = "vm.max_map_count"
    34  	vmSwappinessKey  = "vm.swappiness"
    35  	fsNROpenKey      = "fs.nr_open"
    36  )
    37  
    38  // CanGetProcessLimits returns a boolean to signify if it can return limits,
    39  // and a warning message if it cannot.
    40  func CanGetProcessLimits() (bool, string) {
    41  	return true, ""
    42  }
    43  
    44  // GetProcessLimits returns the known process limits.
    45  func GetProcessLimits() (ProcessLimits, error) {
    46  	var noFile syscall.Rlimit
    47  	err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &noFile)
    48  	if err != nil {
    49  		return ProcessLimits{}, err
    50  	}
    51  
    52  	maxMap, err := sysctlInt64(vmMaxMapCountKey)
    53  	if err != nil {
    54  		return ProcessLimits{}, err
    55  	}
    56  
    57  	swap, err := sysctlInt64(vmSwappinessKey)
    58  	if err != nil {
    59  		return ProcessLimits{}, err
    60  	}
    61  
    62  	return ProcessLimits{
    63  		NoFileCurr:    noFile.Cur,
    64  		NoFileMax:     noFile.Max,
    65  		VMMaxMapCount: maxMap,
    66  		VMSwappiness:  swap,
    67  	}, nil
    68  }
    69  
    70  // RaiseProcessNoFileToNROpen first determines the NROpen limit by reading
    71  // the corresponding proc sys file and then if the hard or soft limits
    72  // are below this number, the limits are raised using a call to setrlimit.
    73  func RaiseProcessNoFileToNROpen() (RaiseProcessNoFileToNROpenResult, error) {
    74  	value, err := sysctlInt64(fsNROpenKey)
    75  	if err != nil {
    76  		return RaiseProcessNoFileToNROpenResult{}, fmt.Errorf(
    77  			"unable to raise nofile limits: nr_open_parse_err=%v", err)
    78  	}
    79  
    80  	limit := uint64(value)
    81  
    82  	var limits syscall.Rlimit
    83  	if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limits); err != nil {
    84  		return RaiseProcessNoFileToNROpenResult{}, fmt.Errorf(
    85  			"unable to raise nofile limits: rlimit_get_err=%v", err)
    86  	}
    87  
    88  	if limits.Max >= limit && limits.Cur >= limit {
    89  		// Limit already set correctly
    90  		return RaiseProcessNoFileToNROpenResult{
    91  			RaisePerformed:  false,
    92  			NROpenValue:     limit,
    93  			NoFileMaxValue:  limits.Max,
    94  			NoFileCurrValue: limits.Cur,
    95  		}, nil
    96  	}
    97  
    98  	limits.Max = limit
    99  	limits.Cur = limit
   100  
   101  	if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limits); err != nil {
   102  		return RaiseProcessNoFileToNROpenResult{}, fmt.Errorf(
   103  			"unable to raise nofile limits: rlimit_set_err=%v", err)
   104  	}
   105  
   106  	return RaiseProcessNoFileToNROpenResult{
   107  		RaisePerformed:  true,
   108  		NROpenValue:     limit,
   109  		NoFileMaxValue:  limits.Max,
   110  		NoFileCurrValue: limits.Cur,
   111  	}, nil
   112  }
   113  
   114  func sysctlInt64(key string) (int64, error) {
   115  	str, err := sysctl(key)
   116  	if err != nil {
   117  		return 0, err
   118  	}
   119  
   120  	num, err := strconv.Atoi(str)
   121  	if err != nil {
   122  		return 0, err
   123  	}
   124  
   125  	return int64(num), nil
   126  }
   127  
   128  func sysctl(key string) (string, error) {
   129  	path := sysctlDir + strings.Replace(key, ".", "/", -1)
   130  	data, err := ioutil.ReadFile(path)
   131  	if err != nil {
   132  		return "", fmt.Errorf("could not find the given sysctl file: %v, err: %v", path, err)
   133  	}
   134  	return strings.TrimSpace(string(data)), nil
   135  }