LCOV - code coverage report
Current view: top level - shared - dropin.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 53 116 45.7 %
Date: 2019-08-23 13:36:53 Functions: 3 6 50.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 42 126 33.3 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <errno.h>
       4                 :            : #include <stdarg.h>
       5                 :            : #include <stdio.h>
       6                 :            : #include <stdlib.h>
       7                 :            : 
       8                 :            : #include "alloc-util.h"
       9                 :            : #include "conf-files.h"
      10                 :            : #include "dirent-util.h"
      11                 :            : #include "dropin.h"
      12                 :            : #include "escape.h"
      13                 :            : #include "fd-util.h"
      14                 :            : #include "fileio-label.h"
      15                 :            : #include "fs-util.h"
      16                 :            : #include "hashmap.h"
      17                 :            : #include "log.h"
      18                 :            : #include "macro.h"
      19                 :            : #include "mkdir.h"
      20                 :            : #include "path-util.h"
      21                 :            : #include "set.h"
      22                 :            : #include "string-util.h"
      23                 :            : #include "strv.h"
      24                 :            : #include "unit-name.h"
      25                 :            : 
      26                 :          0 : int drop_in_file(const char *dir, const char *unit, unsigned level,
      27                 :            :                  const char *name, char **ret_p, char **ret_q) {
      28                 :            : 
      29                 :            :         char prefix[DECIMAL_STR_MAX(unsigned)];
      30                 :          0 :         _cleanup_free_ char *b = NULL, *p = NULL, *q = NULL;
      31                 :            : 
      32         [ #  # ]:          0 :         assert(unit);
      33         [ #  # ]:          0 :         assert(name);
      34         [ #  # ]:          0 :         assert(ret_p);
      35         [ #  # ]:          0 :         assert(ret_q);
      36                 :            : 
      37                 :          0 :         sprintf(prefix, "%u", level);
      38                 :            : 
      39                 :          0 :         b = xescape(name, "/.");
      40         [ #  # ]:          0 :         if (!b)
      41                 :          0 :                 return -ENOMEM;
      42                 :            : 
      43         [ #  # ]:          0 :         if (!filename_is_valid(b))
      44                 :          0 :                 return -EINVAL;
      45                 :            : 
      46                 :          0 :         p = strjoin(dir, "/", unit, ".d");
      47                 :          0 :         q = strjoin(p, "/", prefix, "-", b, ".conf");
      48   [ #  #  #  # ]:          0 :         if (!p || !q)
      49                 :          0 :                 return -ENOMEM;
      50                 :            : 
      51                 :          0 :         *ret_p = TAKE_PTR(p);
      52                 :          0 :         *ret_q = TAKE_PTR(q);
      53                 :          0 :         return 0;
      54                 :            : }
      55                 :            : 
      56                 :          0 : int write_drop_in(const char *dir, const char *unit, unsigned level,
      57                 :            :                   const char *name, const char *data) {
      58                 :            : 
      59                 :          0 :         _cleanup_free_ char *p = NULL, *q = NULL;
      60                 :            :         int r;
      61                 :            : 
      62         [ #  # ]:          0 :         assert(dir);
      63         [ #  # ]:          0 :         assert(unit);
      64         [ #  # ]:          0 :         assert(name);
      65         [ #  # ]:          0 :         assert(data);
      66                 :            : 
      67                 :          0 :         r = drop_in_file(dir, unit, level, name, &p, &q);
      68         [ #  # ]:          0 :         if (r < 0)
      69                 :          0 :                 return r;
      70                 :            : 
      71                 :          0 :         (void) mkdir_p(p, 0755);
      72                 :          0 :         return write_string_file_atomic_label(q, data);
      73                 :            : }
      74                 :            : 
      75                 :          0 : int write_drop_in_format(const char *dir, const char *unit, unsigned level,
      76                 :            :                          const char *name, const char *format, ...) {
      77                 :          0 :         _cleanup_free_ char *p = NULL;
      78                 :            :         va_list ap;
      79                 :            :         int r;
      80                 :            : 
      81         [ #  # ]:          0 :         assert(dir);
      82         [ #  # ]:          0 :         assert(unit);
      83         [ #  # ]:          0 :         assert(name);
      84         [ #  # ]:          0 :         assert(format);
      85                 :            : 
      86                 :          0 :         va_start(ap, format);
      87                 :          0 :         r = vasprintf(&p, format, ap);
      88                 :          0 :         va_end(ap);
      89                 :            : 
      90         [ #  # ]:          0 :         if (r < 0)
      91                 :          0 :                 return -ENOMEM;
      92                 :            : 
      93                 :          0 :         return write_drop_in(dir, unit, level, name, p);
      94                 :            : }
      95                 :            : 
      96                 :         16 : static int unit_file_add_dir(
      97                 :            :                 const char *original_root,
      98                 :            :                 const char *path,
      99                 :            :                 char ***dirs) {
     100                 :            : 
     101                 :         16 :         _cleanup_free_ char *chased = NULL;
     102                 :            :         int r;
     103                 :            : 
     104         [ -  + ]:         16 :         assert(path);
     105                 :            : 
     106                 :            :         /* This adds [original_root]/path to dirs, if it exists. */
     107                 :            : 
     108                 :         16 :         r = chase_symlinks(path, original_root, 0, &chased);
     109         [ -  + ]:         16 :         if (r == -ENOENT) /* Ignore -ENOENT, after all most units won't have a drop-in dir. */
     110                 :          0 :                 return 0;
     111         [ -  + ]:         16 :         if (r == -ENAMETOOLONG) {
     112                 :            :                 /* Also, ignore -ENAMETOOLONG but log about it. After all, users are not even able to create the
     113                 :            :                  * drop-in dir in such case. This mostly happens for device units with an overly long /sys path. */
     114         [ #  # ]:          0 :                 log_debug_errno(r, "Path '%s' too long, couldn't canonicalize, ignoring.", path);
     115                 :          0 :                 return 0;
     116                 :            :         }
     117         [ -  + ]:         16 :         if (r < 0)
     118         [ #  # ]:          0 :                 return log_warning_errno(r, "Failed to canonicalize path '%s': %m", path);
     119                 :            : 
     120         [ -  + ]:         16 :         if (strv_consume(dirs, TAKE_PTR(chased)) < 0)
     121                 :          0 :                 return log_oom();
     122                 :            : 
     123                 :         16 :         return 0;
     124                 :            : }
     125                 :            : 
     126                 :     100476 : static int unit_file_find_dirs(
     127                 :            :                 const char *original_root,
     128                 :            :                 Set *unit_path_cache,
     129                 :            :                 const char *unit_path,
     130                 :            :                 const char *name,
     131                 :            :                 const char *suffix,
     132                 :            :                 char ***dirs) {
     133                 :            : 
     134                 :     100476 :         _cleanup_free_ char *prefix = NULL, *instance = NULL, *built = NULL;
     135                 :            :         bool is_instance, chopped;
     136                 :            :         const char *dash;
     137                 :            :         UnitType type;
     138                 :            :         char *path;
     139                 :            :         size_t n;
     140                 :            :         int r;
     141                 :            : 
     142         [ -  + ]:     100476 :         assert(unit_path);
     143         [ -  + ]:     100476 :         assert(name);
     144         [ -  + ]:     100476 :         assert(suffix);
     145                 :            : 
     146   [ +  +  +  -  :     904284 :         path = strjoina(unit_path, "/", name, suffix);
          -  +  -  +  +  
                +  +  - ]
     147   [ +  -  +  + ]:     100476 :         if (!unit_path_cache || set_get(unit_path_cache, path)) {
     148                 :         16 :                 r = unit_file_add_dir(original_root, path, dirs);
     149         [ -  + ]:         16 :                 if (r < 0)
     150                 :          0 :                         return r;
     151                 :            :         }
     152                 :            : 
     153                 :     100476 :         is_instance = unit_name_is_valid(name, UNIT_NAME_INSTANCE);
     154         [ -  + ]:     100476 :         if (is_instance) { /* Also try the template dir */
     155         [ #  # ]:          0 :                 _cleanup_free_ char *template = NULL;
     156                 :            : 
     157                 :          0 :                 r = unit_name_template(name, &template);
     158         [ #  # ]:          0 :                 if (r < 0)
     159         [ #  # ]:          0 :                         return log_error_errno(r, "Failed to generate template from unit name: %m");
     160                 :            : 
     161                 :          0 :                 r = unit_file_find_dirs(original_root, unit_path_cache, unit_path, template, suffix, dirs);
     162         [ #  # ]:          0 :                 if (r < 0)
     163                 :          0 :                         return r;
     164                 :            :         }
     165                 :            : 
     166                 :            :         /* Let's see if there's a "-" prefix for this unit name. If so, let's invoke ourselves for it. This will then
     167                 :            :          * recursively do the same for all our prefixes. i.e. this means given "foo-bar-waldo.service" we'll also
     168                 :            :          * search "foo-bar-.service" and "foo-.service".
     169                 :            :          *
     170                 :            :          * Note the order in which we do it: we traverse up adding drop-ins on each step. This means the more specific
     171                 :            :          * drop-ins may override the more generic drop-ins, which is the intended behaviour. */
     172                 :            : 
     173                 :     100476 :         r = unit_name_to_prefix(name, &prefix);
     174         [ -  + ]:     100476 :         if (r < 0)
     175         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to derive unit name prefix from unit name: %m");
     176                 :            : 
     177                 :     100476 :         chopped = false;
     178                 :            :         for (;;) {
     179                 :     248876 :                 dash = strrchr(prefix, '-');
     180         [ +  + ]:     174676 :                 if (!dash) /* No dash? if so we are done */
     181                 :      25964 :                         return 0;
     182                 :            : 
     183                 :     148712 :                 n = (size_t) (dash - prefix);
     184         [ +  + ]:     148712 :                 if (n == 0) /* Leading dash? If so, we are done */
     185                 :        312 :                         return 0;
     186                 :            : 
     187   [ +  +  -  + ]:     148400 :                 if (prefix[n+1] != 0 || chopped) {
     188                 :      74200 :                         prefix[n+1] = 0;
     189                 :      74200 :                         break;
     190                 :            :                 }
     191                 :            : 
     192                 :            :                 /* Trailing dash? If so, chop it off and try again, but not more than once. */
     193                 :      74200 :                 prefix[n] = 0;
     194                 :      74200 :                 chopped = true;
     195                 :            :         }
     196                 :            : 
     197         [ -  + ]:      74200 :         if (!unit_prefix_is_valid(prefix))
     198                 :          0 :                 return 0;
     199                 :            : 
     200                 :      74200 :         type = unit_name_to_type(name);
     201         [ -  + ]:      74200 :         if (type < 0)
     202         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     203                 :            :                                        "Failed to to derive unit type from unit name: %s",
     204                 :            :                                        name);
     205                 :            : 
     206         [ -  + ]:      74200 :         if (is_instance) {
     207                 :          0 :                 r = unit_name_to_instance(name, &instance);
     208         [ #  # ]:          0 :                 if (r < 0)
     209         [ #  # ]:          0 :                         return log_error_errno(r, "Failed to derive unit name instance from unit name: %m");
     210                 :            :         }
     211                 :            : 
     212                 :      74200 :         r = unit_name_build_from_type(prefix, instance, type, &built);
     213         [ -  + ]:      74200 :         if (r < 0)
     214         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to build prefix unit name: %m");
     215                 :            : 
     216                 :      74200 :         return unit_file_find_dirs(original_root, unit_path_cache, unit_path, built, suffix, dirs);
     217                 :            : }
     218                 :            : 
     219                 :      25844 : int unit_file_find_dropin_paths(
     220                 :            :                 const char *original_root,
     221                 :            :                 char **lookup_path,
     222                 :            :                 Set *unit_path_cache,
     223                 :            :                 const char *dir_suffix,
     224                 :            :                 const char *file_suffix,
     225                 :            :                 const Set *names,
     226                 :            :                 char ***ret) {
     227                 :            : 
     228                 :      25844 :         _cleanup_strv_free_ char **dirs = NULL;
     229                 :            :         char *name, **p;
     230                 :            :         Iterator i;
     231                 :            :         int r;
     232                 :            : 
     233         [ -  + ]:      25844 :         assert(ret);
     234                 :            : 
     235         [ +  + ]:      52120 :         SET_FOREACH(name, names, i)
     236   [ +  -  +  + ]:      52552 :                 STRV_FOREACH(p, lookup_path)
     237                 :      26276 :                         (void) unit_file_find_dirs(original_root, unit_path_cache, *p, name, dir_suffix, &dirs);
     238                 :            : 
     239         [ +  + ]:      25844 :         if (strv_isempty(dirs)) {
     240                 :      25840 :                 *ret = NULL;
     241                 :      25840 :                 return 0;
     242                 :            :         }
     243                 :            : 
     244                 :          4 :         r = conf_files_list_strv(ret, file_suffix, NULL, 0, (const char**) dirs);
     245         [ -  + ]:          4 :         if (r < 0)
     246         [ #  # ]:          0 :                 return log_warning_errno(r, "Failed to create the list of configuration files: %m");
     247                 :            : 
     248                 :          4 :         return 1;
     249                 :            : }

Generated by: LCOV version 1.14