LCOV - code coverage report
Current view: top level - sysctl - sysctl.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 22 169 13.0 %
Date: 2019-08-23 13:36:53 Functions: 5 12 41.7 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 7 155 4.5 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <errno.h>
       4                 :            : #include <getopt.h>
       5                 :            : #include <limits.h>
       6                 :            : #include <stdbool.h>
       7                 :            : #include <stdio.h>
       8                 :            : #include <stdlib.h>
       9                 :            : #include <string.h>
      10                 :            : #include <sys/stat.h>
      11                 :            : #include <sys/types.h>
      12                 :            : 
      13                 :            : #include "conf-files.h"
      14                 :            : #include "def.h"
      15                 :            : #include "fd-util.h"
      16                 :            : #include "fileio.h"
      17                 :            : #include "hashmap.h"
      18                 :            : #include "log.h"
      19                 :            : #include "main-func.h"
      20                 :            : #include "pager.h"
      21                 :            : #include "path-util.h"
      22                 :            : #include "pretty-print.h"
      23                 :            : #include "string-util.h"
      24                 :            : #include "strv.h"
      25                 :            : #include "sysctl-util.h"
      26                 :            : 
      27                 :            : static char **arg_prefixes = NULL;
      28                 :            : static bool arg_cat_config = false;
      29                 :            : static PagerFlags arg_pager_flags = 0;
      30                 :            : 
      31                 :         16 : STATIC_DESTRUCTOR_REGISTER(arg_prefixes, strv_freep);
      32                 :            : 
      33                 :            : typedef struct Option {
      34                 :            :         char *key;
      35                 :            :         char *value;
      36                 :            :         bool ignore_failure;
      37                 :            : } Option;
      38                 :            : 
      39                 :          0 : static Option *option_free(Option *o) {
      40         [ #  # ]:          0 :         if (!o)
      41                 :          0 :                 return NULL;
      42                 :            : 
      43                 :          0 :         free(o->key);
      44                 :          0 :         free(o->value);
      45                 :            : 
      46                 :          0 :         return mfree(o);
      47                 :            : }
      48                 :            : 
      49         [ #  # ]:          0 : DEFINE_TRIVIAL_CLEANUP_FUNC(Option*, option_free);
      50                 :          0 : DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(option_hash_ops, char, string_hash_func, string_compare_func, Option, option_free);
      51                 :            : 
      52                 :          0 : static Option *option_new(
      53                 :            :                 const char *key,
      54                 :            :                 const char *value,
      55                 :            :                 bool ignore_failure) {
      56                 :            : 
      57                 :          0 :         _cleanup_(option_freep) Option *o = NULL;
      58                 :            : 
      59         [ #  # ]:          0 :         assert(key);
      60         [ #  # ]:          0 :         assert(value);
      61                 :            : 
      62                 :          0 :         o = new(Option, 1);
      63         [ #  # ]:          0 :         if (!o)
      64                 :          0 :                 return NULL;
      65                 :            : 
      66                 :          0 :         *o = (Option) {
      67                 :          0 :                 .key = strdup(key),
      68                 :          0 :                 .value = strdup(value),
      69                 :            :                 .ignore_failure = ignore_failure,
      70                 :            :         };
      71                 :            : 
      72   [ #  #  #  # ]:          0 :         if (!o->key || !o->value)
      73                 :          0 :                 return NULL;
      74                 :            : 
      75                 :          0 :         return TAKE_PTR(o);
      76                 :            : }
      77                 :            : 
      78                 :          0 : static int apply_all(OrderedHashmap *sysctl_options) {
      79                 :            :         Option *option;
      80                 :            :         Iterator i;
      81                 :          0 :         int r = 0;
      82                 :            : 
      83         [ #  # ]:          0 :         ORDERED_HASHMAP_FOREACH(option, sysctl_options, i) {
      84                 :            :                 int k;
      85                 :            : 
      86                 :          0 :                 k = sysctl_write(option->key, option->value);
      87         [ #  # ]:          0 :                 if (k < 0) {
      88                 :            :                         /* If the sysctl is not available in the kernel or we are running with reduced
      89                 :            :                          * privileges and cannot write it, then log about the issue at LOG_NOTICE level, and
      90                 :            :                          * proceed without failing. (EROFS is treated as a permission problem here, since
      91                 :            :                          * that's how container managers usually protected their sysctls.) In all other cases
      92                 :            :                          * log an error and make the tool fail. */
      93                 :            : 
      94   [ #  #  #  #  :          0 :                         if (IN_SET(k, -EPERM, -EACCES, -EROFS, -ENOENT) || option->ignore_failure)
                   #  # ]
      95         [ #  # ]:          0 :                                 log_notice_errno(k, "Couldn't write '%s' to '%s', ignoring: %m", option->value, option->key);
      96                 :            :                         else {
      97         [ #  # ]:          0 :                                 log_error_errno(k, "Couldn't write '%s' to '%s': %m", option->value, option->key);
      98         [ #  # ]:          0 :                                 if (r == 0)
      99                 :          0 :                                         r = k;
     100                 :            :                         }
     101                 :            :                 }
     102                 :            :         }
     103                 :            : 
     104                 :          0 :         return r;
     105                 :            : }
     106                 :            : 
     107                 :          0 : static bool test_prefix(const char *p) {
     108                 :            :         char **i;
     109                 :            : 
     110         [ #  # ]:          0 :         if (strv_isempty(arg_prefixes))
     111                 :          0 :                 return true;
     112                 :            : 
     113   [ #  #  #  # ]:          0 :         STRV_FOREACH(i, arg_prefixes) {
     114                 :            :                 const char *t;
     115                 :            : 
     116                 :          0 :                 t = path_startswith(*i, "/proc/sys/");
     117         [ #  # ]:          0 :                 if (!t)
     118                 :          0 :                         t = *i;
     119         [ #  # ]:          0 :                 if (path_startswith(p, t))
     120                 :          0 :                         return true;
     121                 :            :         }
     122                 :            : 
     123                 :          0 :         return false;
     124                 :            : }
     125                 :            : 
     126                 :          0 : static int parse_file(OrderedHashmap *sysctl_options, const char *path, bool ignore_enoent) {
     127                 :          0 :         _cleanup_fclose_ FILE *f = NULL;
     128                 :          0 :         unsigned c = 0;
     129                 :            :         int r;
     130                 :            : 
     131         [ #  # ]:          0 :         assert(path);
     132                 :            : 
     133                 :          0 :         r = search_and_fopen(path, "re", NULL, (const char**) CONF_PATHS_STRV("sysctl.d"), &f);
     134         [ #  # ]:          0 :         if (r < 0) {
     135   [ #  #  #  # ]:          0 :                 if (ignore_enoent && r == -ENOENT)
     136                 :          0 :                         return 0;
     137                 :            : 
     138         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to open file '%s', ignoring: %m", path);
     139                 :            :         }
     140                 :            : 
     141         [ #  # ]:          0 :         log_debug("Parsing %s", path);
     142                 :          0 :         for (;;) {
     143   [ #  #  #  # ]:          0 :                 _cleanup_(option_freep) Option *new_option = NULL;
     144   [ #  #  #  # ]:          0 :                 _cleanup_free_ char *l = NULL;
     145                 :            :                 bool ignore_failure;
     146                 :            :                 Option *existing;
     147                 :            :                 char *p, *value;
     148                 :            :                 int k;
     149                 :            : 
     150                 :          0 :                 k = read_line(f, LONG_LINE_MAX, &l);
     151         [ #  # ]:          0 :                 if (k == 0)
     152                 :          0 :                         break;
     153         [ #  # ]:          0 :                 if (k < 0)
     154         [ #  # ]:          0 :                         return log_error_errno(k, "Failed to read file '%s', ignoring: %m", path);
     155                 :            : 
     156                 :          0 :                 c++;
     157                 :            : 
     158                 :          0 :                 p = strstrip(l);
     159                 :            : 
     160         [ #  # ]:          0 :                 if (isempty(p))
     161                 :          0 :                         continue;
     162         [ #  # ]:          0 :                 if (strchr(COMMENTS "\n", *p))
     163                 :          0 :                         continue;
     164                 :            : 
     165                 :          0 :                 value = strchr(p, '=');
     166         [ #  # ]:          0 :                 if (!value) {
     167         [ #  # ]:          0 :                         log_syntax(NULL, LOG_WARNING, path, c, 0, "Line is not an assignment, ignoring: %s", p);
     168         [ #  # ]:          0 :                         if (r == 0)
     169                 :          0 :                                 r = -EINVAL;
     170                 :          0 :                         continue;
     171                 :            :                 }
     172                 :            : 
     173                 :          0 :                 *value = 0;
     174                 :          0 :                 value++;
     175                 :            : 
     176                 :          0 :                 p = strstrip(p);
     177                 :          0 :                 ignore_failure = p[0] == '-';
     178         [ #  # ]:          0 :                 if (ignore_failure)
     179                 :          0 :                         p++;
     180                 :            : 
     181                 :          0 :                 p = sysctl_normalize(p);
     182                 :          0 :                 value = strstrip(value);
     183                 :            : 
     184         [ #  # ]:          0 :                 if (!test_prefix(p))
     185                 :          0 :                         continue;
     186                 :            : 
     187                 :          0 :                 existing = ordered_hashmap_get(sysctl_options, p);
     188         [ #  # ]:          0 :                 if (existing) {
     189         [ #  # ]:          0 :                         if (streq(value, existing->value)) {
     190   [ #  #  #  # ]:          0 :                                 existing->ignore_failure = existing->ignore_failure || ignore_failure;
     191                 :          0 :                                 continue;
     192                 :            :                         }
     193                 :            : 
     194         [ #  # ]:          0 :                         log_debug("Overwriting earlier assignment of %s at '%s:%u'.", p, path, c);
     195                 :          0 :                         option_free(ordered_hashmap_remove(sysctl_options, p));
     196                 :            :                 }
     197                 :            : 
     198                 :          0 :                 new_option = option_new(p, value, ignore_failure);
     199         [ #  # ]:          0 :                 if (!new_option)
     200                 :          0 :                         return log_oom();
     201                 :            : 
     202                 :          0 :                 k = ordered_hashmap_put(sysctl_options, new_option->key, new_option);
     203         [ #  # ]:          0 :                 if (k < 0)
     204         [ #  # ]:          0 :                         return log_error_errno(k, "Failed to add sysctl variable %s to hashmap: %m", p);
     205                 :            : 
     206                 :          0 :                 TAKE_PTR(new_option);
     207                 :            :         }
     208                 :            : 
     209                 :          0 :         return r;
     210                 :            : }
     211                 :            : 
     212                 :         12 : static int help(void) {
     213                 :         12 :         _cleanup_free_ char *link = NULL;
     214                 :            :         int r;
     215                 :            : 
     216                 :         12 :         r = terminal_urlify_man("systemd-sysctl.service", "8", &link);
     217         [ -  + ]:         12 :         if (r < 0)
     218                 :          0 :                 return log_oom();
     219                 :            : 
     220                 :         12 :         printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
     221                 :            :                "Applies kernel sysctl settings.\n\n"
     222                 :            :                "  -h --help             Show this help\n"
     223                 :            :                "     --version          Show package version\n"
     224                 :            :                "     --cat-config       Show configuration files\n"
     225                 :            :                "     --prefix=PATH      Only apply rules with the specified prefix\n"
     226                 :            :                "     --no-pager         Do not pipe output into a pager\n"
     227                 :            :                "\nSee the %s for details.\n"
     228                 :            :                , program_invocation_short_name
     229                 :            :                , link
     230                 :            :         );
     231                 :            : 
     232                 :         12 :         return 0;
     233                 :            : }
     234                 :            : 
     235                 :         16 : static int parse_argv(int argc, char *argv[]) {
     236                 :            : 
     237                 :            :         enum {
     238                 :            :                 ARG_VERSION = 0x100,
     239                 :            :                 ARG_CAT_CONFIG,
     240                 :            :                 ARG_PREFIX,
     241                 :            :                 ARG_NO_PAGER,
     242                 :            :         };
     243                 :            : 
     244                 :            :         static const struct option options[] = {
     245                 :            :                 { "help",       no_argument,       NULL, 'h'            },
     246                 :            :                 { "version",    no_argument,       NULL, ARG_VERSION    },
     247                 :            :                 { "cat-config", no_argument,       NULL, ARG_CAT_CONFIG },
     248                 :            :                 { "prefix",     required_argument, NULL, ARG_PREFIX     },
     249                 :            :                 { "no-pager",   no_argument,       NULL, ARG_NO_PAGER   },
     250                 :            :                 {}
     251                 :            :         };
     252                 :            : 
     253                 :            :         int c;
     254                 :            : 
     255         [ -  + ]:         16 :         assert(argc >= 0);
     256         [ -  + ]:         16 :         assert(argv);
     257                 :            : 
     258         [ +  - ]:         16 :         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
     259                 :            : 
     260   [ +  -  -  -  :         16 :                 switch (c) {
                -  +  - ]
     261                 :            : 
     262                 :         12 :                 case 'h':
     263                 :         12 :                         return help();
     264                 :            : 
     265                 :          0 :                 case ARG_VERSION:
     266                 :          0 :                         return version();
     267                 :            : 
     268                 :          0 :                 case ARG_CAT_CONFIG:
     269                 :          0 :                         arg_cat_config = true;
     270                 :          0 :                         break;
     271                 :            : 
     272                 :          0 :                 case ARG_PREFIX: {
     273                 :            :                         char *p;
     274                 :            : 
     275                 :            :                         /* We used to require people to specify absolute paths
     276                 :            :                          * in /proc/sys in the past. This is kinda useless, but
     277                 :            :                          * we need to keep compatibility. We now support any
     278                 :            :                          * sysctl name available. */
     279                 :          0 :                         sysctl_normalize(optarg);
     280                 :            : 
     281         [ #  # ]:          0 :                         if (path_startswith(optarg, "/proc/sys"))
     282                 :          0 :                                 p = strdup(optarg);
     283                 :            :                         else
     284                 :          0 :                                 p = path_join("/proc/sys", optarg);
     285         [ #  # ]:          0 :                         if (!p)
     286                 :          0 :                                 return log_oom();
     287                 :            : 
     288         [ #  # ]:          0 :                         if (strv_consume(&arg_prefixes, p) < 0)
     289                 :          0 :                                 return log_oom();
     290                 :            : 
     291                 :          0 :                         break;
     292                 :            :                 }
     293                 :            : 
     294                 :          0 :                 case ARG_NO_PAGER:
     295                 :          0 :                         arg_pager_flags |= PAGER_DISABLE;
     296                 :          0 :                         break;
     297                 :            : 
     298                 :          4 :                 case '?':
     299                 :          4 :                         return -EINVAL;
     300                 :            : 
     301                 :          0 :                 default:
     302                 :          0 :                         assert_not_reached("Unhandled option");
     303                 :            :                 }
     304                 :            : 
     305   [ #  #  #  # ]:          0 :         if (arg_cat_config && argc > optind)
     306         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     307                 :            :                                        "Positional arguments are not allowed with --cat-config");
     308                 :            : 
     309                 :          0 :         return 1;
     310                 :            : }
     311                 :            : 
     312                 :         16 : static int run(int argc, char *argv[]) {
     313                 :         16 :         _cleanup_(ordered_hashmap_freep) OrderedHashmap *sysctl_options = NULL;
     314                 :            :         int r, k;
     315                 :            : 
     316                 :         16 :         r = parse_argv(argc, argv);
     317         [ +  - ]:         16 :         if (r <= 0)
     318                 :         16 :                 return r;
     319                 :            : 
     320                 :          0 :         log_setup_service();
     321                 :            : 
     322                 :          0 :         umask(0022);
     323                 :            : 
     324                 :          0 :         sysctl_options = ordered_hashmap_new(&option_hash_ops);
     325         [ #  # ]:          0 :         if (!sysctl_options)
     326                 :          0 :                 return log_oom();
     327                 :            : 
     328         [ #  # ]:          0 :         if (argc > optind) {
     329                 :            :                 int i;
     330                 :            : 
     331                 :          0 :                 r = 0;
     332                 :            : 
     333         [ #  # ]:          0 :                 for (i = optind; i < argc; i++) {
     334                 :          0 :                         k = parse_file(sysctl_options, argv[i], false);
     335   [ #  #  #  # ]:          0 :                         if (k < 0 && r == 0)
     336                 :          0 :                                 r = k;
     337                 :            :                 }
     338                 :            :         } else {
     339         [ #  # ]:          0 :                 _cleanup_strv_free_ char **files = NULL;
     340                 :            :                 char **f;
     341                 :            : 
     342                 :          0 :                 r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char**) CONF_PATHS_STRV("sysctl.d"));
     343         [ #  # ]:          0 :                 if (r < 0)
     344         [ #  # ]:          0 :                         return log_error_errno(r, "Failed to enumerate sysctl.d files: %m");
     345                 :            : 
     346         [ #  # ]:          0 :                 if (arg_cat_config) {
     347                 :          0 :                         (void) pager_open(arg_pager_flags);
     348                 :            : 
     349                 :          0 :                         return cat_files(NULL, files, 0);
     350                 :            :                 }
     351                 :            : 
     352   [ #  #  #  # ]:          0 :                 STRV_FOREACH(f, files) {
     353                 :          0 :                         k = parse_file(sysctl_options, *f, true);
     354   [ #  #  #  # ]:          0 :                         if (k < 0 && r == 0)
     355                 :          0 :                                 r = k;
     356                 :            :                 }
     357                 :            :         }
     358                 :            : 
     359                 :          0 :         k = apply_all(sysctl_options);
     360   [ #  #  #  # ]:          0 :         if (k < 0 && r == 0)
     361                 :          0 :                 r = k;
     362                 :            : 
     363                 :          0 :         return r;
     364                 :            : }
     365                 :            : 
     366                 :         16 : DEFINE_MAIN_FUNCTION(run);

Generated by: LCOV version 1.14