LCOV - code coverage report
Current view: top level - basic - rlimit-util.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 136 194 70.1 %
Date: 2019-08-23 13:36:53 Functions: 14 16 87.5 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 88 160 55.0 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <errno.h>
       4                 :            : #include <sys/resource.h>
       5                 :            : 
       6                 :            : #include "alloc-util.h"
       7                 :            : #include "extract-word.h"
       8                 :            : #include "fd-util.h"
       9                 :            : #include "format-util.h"
      10                 :            : #include "macro.h"
      11                 :            : #include "missing.h"
      12                 :            : #include "rlimit-util.h"
      13                 :            : #include "string-table.h"
      14                 :            : #include "time-util.h"
      15                 :            : 
      16                 :        451 : int setrlimit_closest(int resource, const struct rlimit *rlim) {
      17                 :            :         struct rlimit highest, fixed;
      18                 :            : 
      19         [ -  + ]:        451 :         assert(rlim);
      20                 :            : 
      21         [ +  + ]:        451 :         if (setrlimit(resource, rlim) >= 0)
      22                 :        443 :                 return 0;
      23                 :            : 
      24         [ +  + ]:          8 :         if (errno != EPERM)
      25                 :          4 :                 return -errno;
      26                 :            : 
      27                 :            :         /* So we failed to set the desired setrlimit, then let's try
      28                 :            :          * to get as close as we can */
      29         [ -  + ]:          4 :         if (getrlimit(resource, &highest) < 0)
      30                 :          0 :                 return -errno;
      31                 :            : 
      32                 :            :         /* If the hard limit is unbounded anyway, then the EPERM had other reasons, let's propagate the original EPERM
      33                 :            :          * then */
      34         [ -  + ]:          4 :         if (highest.rlim_max == RLIM_INFINITY)
      35                 :          0 :                 return -EPERM;
      36                 :            : 
      37                 :          4 :         fixed = (struct rlimit) {
      38                 :          4 :                 .rlim_cur = MIN(rlim->rlim_cur, highest.rlim_max),
      39                 :          4 :                 .rlim_max = MIN(rlim->rlim_max, highest.rlim_max),
      40                 :            :         };
      41                 :            : 
      42                 :            :         /* Shortcut things if we wouldn't change anything. */
      43         [ -  + ]:          4 :         if (fixed.rlim_cur == highest.rlim_cur &&
      44         [ #  # ]:          0 :             fixed.rlim_max == highest.rlim_max)
      45                 :          0 :                 return 0;
      46                 :            : 
      47         [ -  + ]:          4 :         if (setrlimit(resource, &fixed) < 0)
      48                 :          0 :                 return -errno;
      49                 :            : 
      50                 :          4 :         return 0;
      51                 :            : }
      52                 :            : 
      53                 :          0 : int setrlimit_closest_all(const struct rlimit *const *rlim, int *which_failed) {
      54                 :            :         int i, r;
      55                 :            : 
      56         [ #  # ]:          0 :         assert(rlim);
      57                 :            : 
      58                 :            :         /* On failure returns the limit's index that failed in *which_failed, but only if non-NULL */
      59                 :            : 
      60         [ #  # ]:          0 :         for (i = 0; i < _RLIMIT_MAX; i++) {
      61         [ #  # ]:          0 :                 if (!rlim[i])
      62                 :          0 :                         continue;
      63                 :            : 
      64                 :          0 :                 r = setrlimit_closest(i, rlim[i]);
      65         [ #  # ]:          0 :                 if (r < 0) {
      66         [ #  # ]:          0 :                         if (which_failed)
      67                 :          0 :                                 *which_failed = i;
      68                 :            : 
      69                 :          0 :                         return r;
      70                 :            :                 }
      71                 :            :         }
      72                 :            : 
      73         [ #  # ]:          0 :         if (which_failed)
      74                 :          0 :                 *which_failed = -1;
      75                 :            : 
      76                 :          0 :         return 0;
      77                 :            : }
      78                 :            : 
      79                 :        124 : static int rlimit_parse_u64(const char *val, rlim_t *ret) {
      80                 :            :         uint64_t u;
      81                 :            :         int r;
      82                 :            : 
      83         [ -  + ]:        124 :         assert(val);
      84         [ -  + ]:        124 :         assert(ret);
      85                 :            : 
      86         [ +  + ]:        124 :         if (streq(val, "infinity")) {
      87                 :         40 :                 *ret = RLIM_INFINITY;
      88                 :         40 :                 return 0;
      89                 :            :         }
      90                 :            : 
      91                 :            :         /* setrlimit(2) suggests rlim_t is always 64bit on Linux. */
      92                 :            :         assert_cc(sizeof(rlim_t) == sizeof(uint64_t));
      93                 :            : 
      94                 :         84 :         r = safe_atou64(val, &u);
      95         [ +  + ]:         84 :         if (r < 0)
      96                 :         12 :                 return r;
      97         [ -  + ]:         72 :         if (u >= (uint64_t) RLIM_INFINITY)
      98                 :          0 :                 return -ERANGE;
      99                 :            : 
     100                 :         72 :         *ret = (rlim_t) u;
     101                 :         72 :         return 0;
     102                 :            : }
     103                 :            : 
     104                 :          0 : static int rlimit_parse_size(const char *val, rlim_t *ret) {
     105                 :            :         uint64_t u;
     106                 :            :         int r;
     107                 :            : 
     108         [ #  # ]:          0 :         assert(val);
     109         [ #  # ]:          0 :         assert(ret);
     110                 :            : 
     111         [ #  # ]:          0 :         if (streq(val, "infinity")) {
     112                 :          0 :                 *ret = RLIM_INFINITY;
     113                 :          0 :                 return 0;
     114                 :            :         }
     115                 :            : 
     116                 :          0 :         r = parse_size(val, 1024, &u);
     117         [ #  # ]:          0 :         if (r < 0)
     118                 :          0 :                 return r;
     119         [ #  # ]:          0 :         if (u >= (uint64_t) RLIM_INFINITY)
     120                 :          0 :                 return -ERANGE;
     121                 :            : 
     122                 :          0 :         *ret = (rlim_t) u;
     123                 :          0 :         return 0;
     124                 :            : }
     125                 :            : 
     126                 :         40 : static int rlimit_parse_sec(const char *val, rlim_t *ret) {
     127                 :            :         uint64_t u;
     128                 :            :         usec_t t;
     129                 :            :         int r;
     130                 :            : 
     131         [ -  + ]:         40 :         assert(val);
     132         [ -  + ]:         40 :         assert(ret);
     133                 :            : 
     134         [ +  + ]:         40 :         if (streq(val, "infinity")) {
     135                 :          4 :                 *ret = RLIM_INFINITY;
     136                 :          4 :                 return 0;
     137                 :            :         }
     138                 :            : 
     139                 :         36 :         r = parse_sec(val, &t);
     140         [ -  + ]:         36 :         if (r < 0)
     141                 :          0 :                 return r;
     142         [ -  + ]:         36 :         if (t == USEC_INFINITY) {
     143                 :          0 :                 *ret = RLIM_INFINITY;
     144                 :          0 :                 return 0;
     145                 :            :         }
     146                 :            : 
     147                 :         36 :         u = (uint64_t) DIV_ROUND_UP(t, USEC_PER_SEC);
     148         [ -  + ]:         36 :         if (u >= (uint64_t) RLIM_INFINITY)
     149                 :          0 :                 return -ERANGE;
     150                 :            : 
     151                 :         36 :         *ret = (rlim_t) u;
     152                 :         36 :         return 0;
     153                 :            : }
     154                 :            : 
     155                 :         40 : static int rlimit_parse_usec(const char *val, rlim_t *ret) {
     156                 :            :         usec_t t;
     157                 :            :         int r;
     158                 :            : 
     159         [ -  + ]:         40 :         assert(val);
     160         [ -  + ]:         40 :         assert(ret);
     161                 :            : 
     162         [ +  + ]:         40 :         if (streq(val, "infinity")) {
     163                 :         12 :                 *ret = RLIM_INFINITY;
     164                 :         12 :                 return 0;
     165                 :            :         }
     166                 :            : 
     167                 :         28 :         r = parse_time(val, &t, 1);
     168         [ -  + ]:         28 :         if (r < 0)
     169                 :          0 :                 return r;
     170         [ -  + ]:         28 :         if (t == USEC_INFINITY) {
     171                 :          0 :                 *ret = RLIM_INFINITY;
     172                 :          0 :                 return 0;
     173                 :            :         }
     174                 :            : 
     175                 :         28 :         *ret = (rlim_t) t;
     176                 :         28 :         return 0;
     177                 :            : }
     178                 :            : 
     179                 :         84 : static int rlimit_parse_nice(const char *val, rlim_t *ret) {
     180                 :            :         uint64_t rl;
     181                 :            :         int r;
     182                 :            : 
     183                 :            :         /* So, Linux is weird. The range for RLIMIT_NICE is 40..1, mapping to the nice levels -20..19. However, the
     184                 :            :          * RLIMIT_NICE limit defaults to 0 by the kernel, i.e. a value that maps to nice level 20, which of course is
     185                 :            :          * bogus and does not exist. In order to permit parsing the RLIMIT_NICE of 0 here we hence implement a slight
     186                 :            :          * asymmetry: when parsing as positive nice level we permit 0..19. When parsing as negative nice level, we
     187                 :            :          * permit -20..0. But when parsing as raw resource limit value then we also allow the special value 0.
     188                 :            :          *
     189                 :            :          * Yeah, Linux is quality engineering sometimes... */
     190                 :            : 
     191         [ +  + ]:         84 :         if (val[0] == '+') {
     192                 :            : 
     193                 :            :                 /* Prefixed with "+": Parse as positive user-friendly nice value */
     194                 :         16 :                 r = safe_atou64(val + 1, &rl);
     195         [ -  + ]:         16 :                 if (r < 0)
     196                 :          0 :                         return r;
     197                 :            : 
     198         [ +  + ]:         16 :                 if (rl >= PRIO_MAX)
     199                 :          4 :                         return -ERANGE;
     200                 :            : 
     201                 :         12 :                 rl = 20 - rl;
     202                 :            : 
     203         [ +  + ]:         68 :         } else if (val[0] == '-') {
     204                 :            : 
     205                 :            :                 /* Prefixed with "-": Parse as negative user-friendly nice value */
     206                 :         16 :                 r = safe_atou64(val + 1, &rl);
     207         [ -  + ]:         16 :                 if (r < 0)
     208                 :          0 :                         return r;
     209                 :            : 
     210         [ +  + ]:         16 :                 if (rl > (uint64_t) (-PRIO_MIN))
     211                 :          4 :                         return -ERANGE;
     212                 :            : 
     213                 :         12 :                 rl = 20 + rl;
     214                 :            :         } else {
     215                 :            : 
     216                 :            :                 /* Not prefixed: parse as raw resource limit value */
     217                 :         52 :                 r = safe_atou64(val, &rl);
     218         [ -  + ]:         52 :                 if (r < 0)
     219                 :          0 :                         return r;
     220                 :            : 
     221         [ +  + ]:         52 :                 if (rl > (uint64_t) (20 - PRIO_MIN))
     222                 :          4 :                         return -ERANGE;
     223                 :            :         }
     224                 :            : 
     225                 :         72 :         *ret = (rlim_t) rl;
     226                 :         72 :         return 0;
     227                 :            : }
     228                 :            : 
     229                 :            : static int (*const rlimit_parse_table[_RLIMIT_MAX])(const char *val, rlim_t *ret) = {
     230                 :            :         [RLIMIT_CPU] = rlimit_parse_sec,
     231                 :            :         [RLIMIT_FSIZE] = rlimit_parse_size,
     232                 :            :         [RLIMIT_DATA] = rlimit_parse_size,
     233                 :            :         [RLIMIT_STACK] = rlimit_parse_size,
     234                 :            :         [RLIMIT_CORE] = rlimit_parse_size,
     235                 :            :         [RLIMIT_RSS] = rlimit_parse_size,
     236                 :            :         [RLIMIT_NOFILE] = rlimit_parse_u64,
     237                 :            :         [RLIMIT_AS] = rlimit_parse_size,
     238                 :            :         [RLIMIT_NPROC] = rlimit_parse_u64,
     239                 :            :         [RLIMIT_MEMLOCK] = rlimit_parse_size,
     240                 :            :         [RLIMIT_LOCKS] = rlimit_parse_u64,
     241                 :            :         [RLIMIT_SIGPENDING] = rlimit_parse_u64,
     242                 :            :         [RLIMIT_MSGQUEUE] = rlimit_parse_size,
     243                 :            :         [RLIMIT_NICE] = rlimit_parse_nice,
     244                 :            :         [RLIMIT_RTPRIO] = rlimit_parse_u64,
     245                 :            :         [RLIMIT_RTTIME] = rlimit_parse_usec,
     246                 :            : };
     247                 :            : 
     248                 :        288 : int rlimit_parse_one(int resource, const char *val, rlim_t *ret) {
     249         [ -  + ]:        288 :         assert(val);
     250         [ -  + ]:        288 :         assert(ret);
     251                 :            : 
     252         [ -  + ]:        288 :         if (resource < 0)
     253                 :          0 :                 return -EINVAL;
     254         [ -  + ]:        288 :         if (resource >= _RLIMIT_MAX)
     255                 :          0 :                 return -EINVAL;
     256                 :            : 
     257                 :        288 :         return rlimit_parse_table[resource](val, ret);
     258                 :            : }
     259                 :            : 
     260                 :        224 : int rlimit_parse(int resource, const char *val, struct rlimit *ret) {
     261                 :        224 :         _cleanup_free_ char *hard = NULL, *soft = NULL;
     262                 :            :         rlim_t hl, sl;
     263                 :            :         int r;
     264                 :            : 
     265         [ -  + ]:        224 :         assert(val);
     266         [ -  + ]:        224 :         assert(ret);
     267                 :            : 
     268                 :        224 :         r = extract_first_word(&val, &soft, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
     269         [ -  + ]:        224 :         if (r < 0)
     270                 :          0 :                 return r;
     271         [ -  + ]:        224 :         if (r == 0)
     272                 :          0 :                 return -EINVAL;
     273                 :            : 
     274                 :        224 :         r = rlimit_parse_one(resource, soft, &sl);
     275         [ +  + ]:        224 :         if (r < 0)
     276                 :         20 :                 return r;
     277                 :            : 
     278                 :        204 :         r = extract_first_word(&val, &hard, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
     279         [ -  + ]:        204 :         if (r < 0)
     280                 :          0 :                 return r;
     281         [ +  + ]:        204 :         if (!isempty(val))
     282                 :          8 :                 return -EINVAL;
     283         [ +  + ]:        196 :         if (r == 0)
     284                 :        132 :                 hl = sl;
     285                 :            :         else {
     286                 :         64 :                 r = rlimit_parse_one(resource, hard, &hl);
     287         [ +  + ]:         64 :                 if (r < 0)
     288                 :          4 :                         return r;
     289         [ +  + ]:         60 :                 if (sl > hl)
     290                 :          8 :                         return -EILSEQ;
     291                 :            :         }
     292                 :            : 
     293                 :        184 :         *ret = (struct rlimit) {
     294                 :            :                 .rlim_cur = sl,
     295                 :            :                 .rlim_max = hl,
     296                 :            :         };
     297                 :            : 
     298                 :        184 :         return 0;
     299                 :            : }
     300                 :            : 
     301                 :         60 : int rlimit_format(const struct rlimit *rl, char **ret) {
     302                 :         60 :         char *s = NULL;
     303                 :            : 
     304         [ -  + ]:         60 :         assert(rl);
     305         [ -  + ]:         60 :         assert(ret);
     306                 :            : 
     307   [ +  +  +  - ]:         60 :         if (rl->rlim_cur >= RLIM_INFINITY && rl->rlim_max >= RLIM_INFINITY)
     308                 :          8 :                 s = strdup("infinity");
     309         [ -  + ]:         52 :         else if (rl->rlim_cur >= RLIM_INFINITY)
     310                 :          0 :                 (void) asprintf(&s, "infinity:" RLIM_FMT, rl->rlim_max);
     311         [ +  + ]:         52 :         else if (rl->rlim_max >= RLIM_INFINITY)
     312                 :          4 :                 (void) asprintf(&s, RLIM_FMT ":infinity", rl->rlim_cur);
     313         [ +  + ]:         48 :         else if (rl->rlim_cur == rl->rlim_max)
     314                 :         40 :                 (void) asprintf(&s, RLIM_FMT, rl->rlim_cur);
     315                 :            :         else
     316                 :          8 :                 (void) asprintf(&s, RLIM_FMT ":" RLIM_FMT, rl->rlim_cur, rl->rlim_max);
     317                 :            : 
     318         [ -  + ]:         60 :         if (!s)
     319                 :          0 :                 return -ENOMEM;
     320                 :            : 
     321                 :         60 :         *ret = s;
     322                 :         60 :         return 0;
     323                 :            : }
     324                 :            : 
     325                 :            : static const char* const rlimit_table[_RLIMIT_MAX] = {
     326                 :            :         [RLIMIT_AS]         = "AS",
     327                 :            :         [RLIMIT_CORE]       = "CORE",
     328                 :            :         [RLIMIT_CPU]        = "CPU",
     329                 :            :         [RLIMIT_DATA]       = "DATA",
     330                 :            :         [RLIMIT_FSIZE]      = "FSIZE",
     331                 :            :         [RLIMIT_LOCKS]      = "LOCKS",
     332                 :            :         [RLIMIT_MEMLOCK]    = "MEMLOCK",
     333                 :            :         [RLIMIT_MSGQUEUE]   = "MSGQUEUE",
     334                 :            :         [RLIMIT_NICE]       = "NICE",
     335                 :            :         [RLIMIT_NOFILE]     = "NOFILE",
     336                 :            :         [RLIMIT_NPROC]      = "NPROC",
     337                 :            :         [RLIMIT_RSS]        = "RSS",
     338                 :            :         [RLIMIT_RTPRIO]     = "RTPRIO",
     339                 :            :         [RLIMIT_RTTIME]     = "RTTIME",
     340                 :            :         [RLIMIT_SIGPENDING] = "SIGPENDING",
     341                 :            :         [RLIMIT_STACK]      = "STACK",
     342                 :            : };
     343                 :            : 
     344   [ +  +  +  + ]:        640 : DEFINE_STRING_TABLE_LOOKUP(rlimit, int);
     345                 :            : 
     346                 :        212 : int rlimit_from_string_harder(const char *s) {
     347                 :            :         const char *suffix;
     348                 :            : 
     349                 :            :         /* The official prefix */
     350                 :        212 :         suffix = startswith(s, "RLIMIT_");
     351         [ +  + ]:        212 :         if (suffix)
     352                 :         68 :                 return rlimit_from_string(suffix);
     353                 :            : 
     354                 :            :         /* Our own unit file setting prefix */
     355                 :        144 :         suffix = startswith(s, "Limit");
     356         [ +  + ]:        144 :         if (suffix)
     357                 :         68 :                 return rlimit_from_string(suffix);
     358                 :            : 
     359                 :         76 :         return rlimit_from_string(s);
     360                 :            : }
     361                 :            : 
     362                 :       2308 : void rlimit_free_all(struct rlimit **rl) {
     363                 :            :         int i;
     364                 :            : 
     365         [ -  + ]:       2308 :         if (!rl)
     366                 :          0 :                 return;
     367                 :            : 
     368         [ +  + ]:      39236 :         for (i = 0; i < _RLIMIT_MAX; i++)
     369                 :      36928 :                 rl[i] = mfree(rl[i]);
     370                 :            : }
     371                 :            : 
     372                 :        439 : int rlimit_nofile_bump(int limit) {
     373                 :            :         int r;
     374                 :            : 
     375                 :            :         /* Bumps the (soft) RLIMIT_NOFILE resource limit as close as possible to the specified limit. If a negative
     376                 :            :          * limit is specified, bumps it to the maximum the kernel and the hard resource limit allows. This call should
     377                 :            :          * be used by all our programs that might need a lot of fds, and that know how to deal with high fd numbers
     378                 :            :          * (i.e. do not use select() — which chokes on fds >= 1024) */
     379                 :            : 
     380         [ -  + ]:        439 :         if (limit < 0)
     381                 :          0 :                 limit = read_nr_open();
     382                 :            : 
     383         [ -  + ]:        439 :         if (limit < 3)
     384                 :          0 :                 limit = 3;
     385                 :            : 
     386                 :        439 :         r = setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(limit));
     387         [ -  + ]:        439 :         if (r < 0)
     388         [ #  # ]:          0 :                 return log_debug_errno(r, "Failed to set RLIMIT_NOFILE: %m");
     389                 :            : 
     390                 :        439 :         return 0;
     391                 :            : }
     392                 :            : 
     393                 :         84 : int rlimit_nofile_safe(void) {
     394                 :            :         struct rlimit rl;
     395                 :            : 
     396                 :            :         /* Resets RLIMIT_NOFILE's soft limit FD_SETSIZE (i.e. 1024), for compatibility with software still using
     397                 :            :          * select() */
     398                 :            : 
     399         [ -  + ]:         84 :         if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
     400         [ #  # ]:          0 :                 return log_debug_errno(errno, "Failed to query RLIMIT_NOFILE: %m");
     401                 :            : 
     402         [ +  - ]:         84 :         if (rl.rlim_cur <= FD_SETSIZE)
     403                 :         84 :                 return 0;
     404                 :            : 
     405                 :          0 :         rl.rlim_cur = FD_SETSIZE;
     406         [ #  # ]:          0 :         if (setrlimit(RLIMIT_NOFILE, &rl) < 0)
     407         [ #  # ]:          0 :                 return log_debug_errno(errno, "Failed to lower RLIMIT_NOFILE's soft limit to " RLIM_FMT ": %m", rl.rlim_cur);
     408                 :            : 
     409                 :          0 :         return 1;
     410                 :            : }

Generated by: LCOV version 1.14