Home | History | Annotate | Line # | Download | only in unix
      1 /*	$NetBSD: resource.c,v 1.1 2024/02/18 20:57:57 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * SPDX-License-Identifier: MPL-2.0
      7  *
      8  * This Source Code Form is subject to the terms of the Mozilla Public
      9  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  *
     12  * See the COPYRIGHT file distributed with this work for additional
     13  * information regarding copyright ownership.
     14  */
     15 
     16 #include <inttypes.h>
     17 #include <stdbool.h>
     18 #include <sys/resource.h>
     19 #include <sys/time.h> /* Required on some systems for <sys/resource.h>. */
     20 #include <sys/types.h>
     21 
     22 #include <isc/platform.h>
     23 #include <isc/resource.h>
     24 #include <isc/result.h>
     25 #include <isc/util.h>
     26 
     27 #ifdef __linux__
     28 #include <linux/fs.h> /* To get the large NR_OPEN. */
     29 #endif		      /* ifdef __linux__ */
     30 
     31 #include "errno2result.h"
     32 
     33 static isc_result_t
     34 resource2rlim(isc_resource_t resource, int *rlim_resource) {
     35 	isc_result_t result = ISC_R_SUCCESS;
     36 
     37 	switch (resource) {
     38 	case isc_resource_coresize:
     39 		*rlim_resource = RLIMIT_CORE;
     40 		break;
     41 	case isc_resource_cputime:
     42 		*rlim_resource = RLIMIT_CPU;
     43 		break;
     44 	case isc_resource_datasize:
     45 		*rlim_resource = RLIMIT_DATA;
     46 		break;
     47 	case isc_resource_filesize:
     48 		*rlim_resource = RLIMIT_FSIZE;
     49 		break;
     50 	case isc_resource_lockedmemory:
     51 #ifdef RLIMIT_MEMLOCK
     52 		*rlim_resource = RLIMIT_MEMLOCK;
     53 #else  /* ifdef RLIMIT_MEMLOCK */
     54 		result = ISC_R_NOTIMPLEMENTED;
     55 #endif /* ifdef RLIMIT_MEMLOCK */
     56 		break;
     57 	case isc_resource_openfiles:
     58 #ifdef RLIMIT_NOFILE
     59 		*rlim_resource = RLIMIT_NOFILE;
     60 #else  /* ifdef RLIMIT_NOFILE */
     61 		result = ISC_R_NOTIMPLEMENTED;
     62 #endif /* ifdef RLIMIT_NOFILE */
     63 		break;
     64 	case isc_resource_processes:
     65 #ifdef RLIMIT_NPROC
     66 		*rlim_resource = RLIMIT_NPROC;
     67 #else  /* ifdef RLIMIT_NPROC */
     68 		result = ISC_R_NOTIMPLEMENTED;
     69 #endif /* ifdef RLIMIT_NPROC */
     70 		break;
     71 	case isc_resource_residentsize:
     72 #ifdef RLIMIT_RSS
     73 		*rlim_resource = RLIMIT_RSS;
     74 #else  /* ifdef RLIMIT_RSS */
     75 		result = ISC_R_NOTIMPLEMENTED;
     76 #endif /* ifdef RLIMIT_RSS */
     77 		break;
     78 	case isc_resource_stacksize:
     79 		*rlim_resource = RLIMIT_STACK;
     80 		break;
     81 	default:
     82 		/*
     83 		 * This test is not very robust if isc_resource_t
     84 		 * changes, but generates a clear assertion message.
     85 		 */
     86 		REQUIRE(resource >= isc_resource_coresize &&
     87 			resource <= isc_resource_stacksize);
     88 
     89 		result = ISC_R_RANGE;
     90 		break;
     91 	}
     92 
     93 	return (result);
     94 }
     95 
     96 isc_result_t
     97 isc_resource_setlimit(isc_resource_t resource, isc_resourcevalue_t value) {
     98 	struct rlimit rl;
     99 	rlim_t rlim_value;
    100 	int unixresult;
    101 	int unixresource;
    102 	isc_result_t result;
    103 
    104 	result = resource2rlim(resource, &unixresource);
    105 	if (result != ISC_R_SUCCESS) {
    106 		return (result);
    107 	}
    108 
    109 	if (value == ISC_RESOURCE_UNLIMITED) {
    110 		rlim_value = RLIM_INFINITY;
    111 	} else {
    112 		/*
    113 		 * isc_resourcevalue_t was chosen as an unsigned 64 bit
    114 		 * integer so that it could contain the maximum range of
    115 		 * reasonable values.  Unfortunately, this exceeds the typical
    116 		 * range on Unix systems.  Ensure the range of
    117 		 * rlim_t is not overflowed.
    118 		 */
    119 		isc_resourcevalue_t rlim_max;
    120 		bool rlim_t_is_signed = (((double)(rlim_t)-1) < 0);
    121 
    122 		if (rlim_t_is_signed) {
    123 			rlim_max = ~((rlim_t)1 << (sizeof(rlim_t) * 8 - 1));
    124 		} else {
    125 			rlim_max = (rlim_t)-1;
    126 		}
    127 
    128 		if (value > rlim_max) {
    129 			value = rlim_max;
    130 		}
    131 
    132 		rlim_value = value;
    133 	}
    134 
    135 	rl.rlim_cur = rl.rlim_max = rlim_value;
    136 	unixresult = setrlimit(unixresource, &rl);
    137 
    138 	if (unixresult == 0) {
    139 		return (ISC_R_SUCCESS);
    140 	}
    141 
    142 #if defined(OPEN_MAX) && defined(__APPLE__)
    143 	/*
    144 	 * The Darwin kernel doesn't accept RLIM_INFINITY for rlim_cur; the
    145 	 * maximum possible value is OPEN_MAX.  BIND8 used to use
    146 	 * sysconf(_SC_OPEN_MAX) for such a case, but this value is much
    147 	 * smaller than OPEN_MAX and is not really effective.
    148 	 */
    149 	if (resource == isc_resource_openfiles && rlim_value == RLIM_INFINITY) {
    150 		rl.rlim_cur = OPEN_MAX;
    151 		unixresult = setrlimit(unixresource, &rl);
    152 		if (unixresult == 0) {
    153 			return (ISC_R_SUCCESS);
    154 		}
    155 	}
    156 #elif defined(__linux__)
    157 #ifndef NR_OPEN
    158 #define NR_OPEN (1024 * 1024)
    159 #endif /* ifndef NR_OPEN */
    160 
    161 	/*
    162 	 * Some Linux kernels don't accept RLIM_INFINIT; the maximum
    163 	 * possible value is the NR_OPEN defined in linux/fs.h.
    164 	 */
    165 	if (resource == isc_resource_openfiles && rlim_value == RLIM_INFINITY) {
    166 		rl.rlim_cur = rl.rlim_max = NR_OPEN;
    167 		unixresult = setrlimit(unixresource, &rl);
    168 		if (unixresult == 0) {
    169 			return (ISC_R_SUCCESS);
    170 		}
    171 	}
    172 #endif /* if defined(OPEN_MAX) && defined(__APPLE__) */
    173 	if (resource == isc_resource_openfiles && rlim_value == RLIM_INFINITY) {
    174 		if (getrlimit(unixresource, &rl) == 0) {
    175 			rl.rlim_cur = rl.rlim_max;
    176 			unixresult = setrlimit(unixresource, &rl);
    177 			if (unixresult == 0) {
    178 				return (ISC_R_SUCCESS);
    179 			}
    180 		}
    181 	}
    182 	return (isc__errno2result(errno));
    183 }
    184 
    185 isc_result_t
    186 isc_resource_getlimit(isc_resource_t resource, isc_resourcevalue_t *value) {
    187 	int unixresource;
    188 	struct rlimit rl;
    189 	isc_result_t result;
    190 
    191 	result = resource2rlim(resource, &unixresource);
    192 	if (result != ISC_R_SUCCESS) {
    193 		return (result);
    194 	}
    195 
    196 	if (getrlimit(unixresource, &rl) != 0) {
    197 		return (isc__errno2result(errno));
    198 	}
    199 
    200 	*value = rl.rlim_max;
    201 	return (ISC_R_SUCCESS);
    202 }
    203 
    204 isc_result_t
    205 isc_resource_getcurlimit(isc_resource_t resource, isc_resourcevalue_t *value) {
    206 	int unixresource;
    207 	struct rlimit rl;
    208 	isc_result_t result;
    209 
    210 	result = resource2rlim(resource, &unixresource);
    211 	if (result != ISC_R_SUCCESS) {
    212 		return (result);
    213 	}
    214 
    215 	if (getrlimit(unixresource, &rl) != 0) {
    216 		return (isc__errno2result(errno));
    217 	}
    218 
    219 	*value = rl.rlim_cur;
    220 	return (ISC_R_SUCCESS);
    221 }
    222