LCOV - code coverage report
Current view: top level - sysctl - sysctl.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 22 169 13.0 %
Date: 2019-08-22 15:41:25 Functions: 5 12 41.7 %

          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           4 : 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           3 : static int help(void) {
     213           3 :         _cleanup_free_ char *link = NULL;
     214             :         int r;
     215             : 
     216           3 :         r = terminal_urlify_man("systemd-sysctl.service", "8", &link);
     217           3 :         if (r < 0)
     218           0 :                 return log_oom();
     219             : 
     220           3 :         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           3 :         return 0;
     233             : }
     234             : 
     235           4 : 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           4 :         assert(argc >= 0);
     256           4 :         assert(argv);
     257             : 
     258           4 :         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
     259             : 
     260           4 :                 switch (c) {
     261             : 
     262           3 :                 case 'h':
     263           3 :                         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           1 :                 case '?':
     299           1 :                         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           4 : static int run(int argc, char *argv[]) {
     313           4 :         _cleanup_(ordered_hashmap_freep) OrderedHashmap *sysctl_options = NULL;
     314             :         int r, k;
     315             : 
     316           4 :         r = parse_argv(argc, argv);
     317           4 :         if (r <= 0)
     318           4 :                 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           4 : DEFINE_MAIN_FUNCTION(run);

Generated by: LCOV version 1.14