LCOV - code coverage report
Current view: top level - shared - generator.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 11 193 5.7 %
Date: 2019-08-22 15:41:25 Functions: 2 12 16.7 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <errno.h>
       4             : #include <unistd.h>
       5             : 
       6             : #include "alloc-util.h"
       7             : #include "dropin.h"
       8             : #include "escape.h"
       9             : #include "fd-util.h"
      10             : #include "fileio.h"
      11             : #include "fstab-util.h"
      12             : #include "generator.h"
      13             : #include "log.h"
      14             : #include "macro.h"
      15             : #include "mkdir.h"
      16             : #include "path-util.h"
      17             : #include "special.h"
      18             : #include "specifier.h"
      19             : #include "string-util.h"
      20             : #include "time-util.h"
      21             : #include "unit-name.h"
      22             : #include "util.h"
      23             : 
      24           0 : int generator_open_unit_file(
      25             :                 const char *dest,
      26             :                 const char *source,
      27             :                 const char *name,
      28             :                 FILE **file) {
      29             : 
      30             :         const char *unit;
      31             :         FILE *f;
      32             :         int r;
      33             : 
      34           0 :         unit = prefix_roota(dest, name);
      35             : 
      36           0 :         r = fopen_unlocked(unit, "wxe", &f);
      37           0 :         if (r < 0) {
      38           0 :                 if (source && r == -EEXIST)
      39           0 :                         return log_error_errno(r,
      40             :                                                "Failed to create unit file %s, as it already exists. Duplicate entry in %s?",
      41             :                                                unit, source);
      42             :                 else
      43           0 :                         return log_error_errno(r,
      44             :                                                "Failed to create unit file %s: %m",
      45             :                                                unit);
      46             :         }
      47             : 
      48           0 :         fprintf(f,
      49             :                 "# Automatically generated by %s\n\n",
      50             :                 program_invocation_short_name);
      51             : 
      52           0 :         *file = f;
      53           0 :         return 0;
      54             : }
      55             : 
      56          52 : int generator_add_symlink(const char *dir, const char *dst, const char *dep_type, const char *src) {
      57             :         /* Adds a symlink from <dst>.<dep_type>/ to <src> (if src is absolute)
      58             :          * or ../<src> (otherwise). */
      59             : 
      60             :         const char *from, *to;
      61             : 
      62         260 :         from = path_is_absolute(src) ? src : strjoina("../", src);
      63         780 :         to = strjoina(dir, "/", dst, ".", dep_type, "/", basename(src));
      64             : 
      65          52 :         mkdir_parents_label(to, 0755);
      66          52 :         if (symlink(from, to) < 0)
      67          25 :                 if (errno != EEXIST)
      68           0 :                         return log_error_errno(errno, "Failed to create symlink \"%s\": %m", to);
      69             : 
      70          52 :         return 0;
      71             : }
      72             : 
      73           0 : static int write_fsck_sysroot_service(const char *dir, const char *what) {
      74           0 :         _cleanup_free_ char *device = NULL, *escaped = NULL, *escaped2 = NULL;
      75           0 :         _cleanup_fclose_ FILE *f = NULL;
      76             :         const char *unit;
      77             :         int r;
      78             : 
      79           0 :         escaped = specifier_escape(what);
      80           0 :         if (!escaped)
      81           0 :                 return log_oom();
      82             : 
      83           0 :         escaped2 = cescape(escaped);
      84           0 :         if (!escaped2)
      85           0 :                 return log_oom();
      86             : 
      87           0 :         unit = strjoina(dir, "/"SPECIAL_FSCK_ROOT_SERVICE);
      88           0 :         log_debug("Creating %s", unit);
      89             : 
      90           0 :         r = unit_name_from_path(what, ".device", &device);
      91           0 :         if (r < 0)
      92           0 :                 return log_error_errno(r, "Failed to convert device \"%s\" to unit name: %m", what);
      93             : 
      94           0 :         f = fopen(unit, "wxe");
      95           0 :         if (!f)
      96           0 :                 return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
      97             : 
      98           0 :         fprintf(f,
      99             :                 "# Automatically generated by %1$s\n\n"
     100             :                 "[Unit]\n"
     101             :                 "Description=File System Check on %2$s\n"
     102             :                 "Documentation=man:systemd-fsck-root.service(8)\n"
     103             :                 "DefaultDependencies=no\n"
     104             :                 "BindsTo=%3$s\n"
     105             :                 "Conflicts=shutdown.target\n"
     106             :                 "After=initrd-root-device.target local-fs-pre.target %3$s\n"
     107             :                 "Before=shutdown.target\n"
     108             :                 "\n"
     109             :                 "[Service]\n"
     110             :                 "Type=oneshot\n"
     111             :                 "RemainAfterExit=yes\n"
     112             :                 "ExecStart=" SYSTEMD_FSCK_PATH " %4$s\n"
     113             :                 "TimeoutSec=0\n",
     114             :                 program_invocation_short_name,
     115             :                 escaped,
     116             :                 device,
     117             :                 escaped2);
     118             : 
     119           0 :         r = fflush_and_check(f);
     120           0 :         if (r < 0)
     121           0 :                 return log_error_errno(r, "Failed to write unit file %s: %m", unit);
     122             : 
     123           0 :         return 0;
     124             : }
     125             : 
     126           0 : int generator_write_fsck_deps(
     127             :                 FILE *f,
     128             :                 const char *dir,
     129             :                 const char *what,
     130             :                 const char *where,
     131             :                 const char *fstype) {
     132             : 
     133             :         int r;
     134             : 
     135           0 :         assert(f);
     136           0 :         assert(dir);
     137           0 :         assert(what);
     138           0 :         assert(where);
     139             : 
     140           0 :         if (!is_device_path(what)) {
     141           0 :                 log_warning("Checking was requested for \"%s\", but it is not a device.", what);
     142           0 :                 return 0;
     143             :         }
     144             : 
     145           0 :         if (!isempty(fstype) && !streq(fstype, "auto")) {
     146           0 :                 r = fsck_exists(fstype);
     147           0 :                 if (r < 0)
     148           0 :                         log_warning_errno(r, "Checking was requested for %s, but couldn't detect if fsck.%s may be used, proceeding: %m", what, fstype);
     149           0 :                 else if (r == 0) {
     150             :                         /* treat missing check as essentially OK */
     151           0 :                         log_debug("Checking was requested for %s, but fsck.%s does not exist.", what, fstype);
     152           0 :                         return 0;
     153             :                 }
     154             :         }
     155             : 
     156           0 :         if (path_equal(where, "/")) {
     157             :                 const char *lnk;
     158             : 
     159           0 :                 lnk = strjoina(dir, "/" SPECIAL_LOCAL_FS_TARGET ".wants/" SPECIAL_FSCK_ROOT_SERVICE);
     160             : 
     161           0 :                 (void) mkdir_parents(lnk, 0755);
     162           0 :                 if (symlink(SYSTEM_DATA_UNIT_PATH "/" SPECIAL_FSCK_ROOT_SERVICE, lnk) < 0)
     163           0 :                         return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
     164             : 
     165             :         } else {
     166           0 :                 _cleanup_free_ char *_fsck = NULL;
     167             :                 const char *fsck, *dep;
     168             : 
     169           0 :                 if (in_initrd() && path_equal(where, "/sysroot")) {
     170           0 :                         r = write_fsck_sysroot_service(dir, what);
     171           0 :                         if (r < 0)
     172           0 :                                 return r;
     173             : 
     174           0 :                         fsck = SPECIAL_FSCK_ROOT_SERVICE;
     175           0 :                         dep = "Requires";
     176             :                 } else {
     177             :                         /* When this is /usr, then let's add a Wants= dependency, otherwise a Requires=
     178             :                          * dependency. Why? We can't possibly unmount /usr during shutdown, but if we have a
     179             :                          * Requires= from /usr onto a fsck@.service unit and that unit is shut down, then
     180             :                          * we'd have to unmount /usr too.  */
     181             : 
     182           0 :                         dep = !in_initrd() && path_equal(where, "/usr") ? "Wants" : "Requires";
     183             : 
     184           0 :                         r = unit_name_from_path_instance("systemd-fsck", what, ".service", &_fsck);
     185           0 :                         if (r < 0)
     186           0 :                                 return log_error_errno(r, "Failed to create fsck service name: %m");
     187             : 
     188           0 :                         fsck = _fsck;
     189             :                 }
     190             : 
     191           0 :                 fprintf(f,
     192             :                         "%1$s=%2$s\n"
     193             :                         "After=%2$s\n",
     194             :                         dep, fsck);
     195             :         }
     196             : 
     197           0 :         return 0;
     198             : }
     199             : 
     200           0 : int generator_write_timeouts(
     201             :                 const char *dir,
     202             :                 const char *what,
     203             :                 const char *where,
     204             :                 const char *opts,
     205             :                 char **filtered) {
     206             : 
     207             :         /* Configure how long we wait for a device that backs a mount point or a
     208             :          * swap partition to show up. This is useful to support endless device timeouts
     209             :          * for devices that show up only after user input, like crypto devices. */
     210             : 
     211           0 :         _cleanup_free_ char *node = NULL, *unit = NULL, *timeout = NULL;
     212             :         usec_t u;
     213             :         int r;
     214             : 
     215           0 :         r = fstab_filter_options(opts, "comment=systemd.device-timeout\0"
     216             :                                        "x-systemd.device-timeout\0",
     217             :                                  NULL, &timeout, filtered);
     218           0 :         if (r <= 0)
     219           0 :                 return r;
     220             : 
     221           0 :         r = parse_sec_fix_0(timeout, &u);
     222           0 :         if (r < 0) {
     223           0 :                 log_warning("Failed to parse timeout for %s, ignoring: %s", where, timeout);
     224           0 :                 return 0;
     225             :         }
     226             : 
     227           0 :         node = fstab_node_to_udev_node(what);
     228           0 :         if (!node)
     229           0 :                 return log_oom();
     230           0 :         if (!is_device_path(node)) {
     231           0 :                 log_warning("x-systemd.device-timeout ignored for %s", what);
     232           0 :                 return 0;
     233             :         }
     234             : 
     235           0 :         r = unit_name_from_path(node, ".device", &unit);
     236           0 :         if (r < 0)
     237           0 :                 return log_error_errno(r, "Failed to make unit name from path: %m");
     238             : 
     239           0 :         return write_drop_in_format(dir, unit, 50, "device-timeout",
     240             :                                     "# Automatically generated by %s\n\n"
     241             :                                     "[Unit]\n"
     242             :                                     "JobRunningTimeoutSec=%s",
     243             :                                     program_invocation_short_name,
     244             :                                     timeout);
     245             : }
     246             : 
     247           0 : int generator_write_device_deps(
     248             :                 const char *dir,
     249             :                 const char *what,
     250             :                 const char *where,
     251             :                 const char *opts) {
     252             : 
     253             :         /* fstab records that specify _netdev option should apply the network
     254             :          * ordering on the actual device depending on network connection. If we
     255             :          * are not mounting real device (NFS, CIFS), we rely on _netdev effect
     256             :          * on the mount unit itself. */
     257             : 
     258           0 :         _cleanup_free_ char *node = NULL, *unit = NULL;
     259             :         int r;
     260             : 
     261           0 :         if (!fstab_test_option(opts, "_netdev\0"))
     262           0 :                 return 0;
     263             : 
     264           0 :         node = fstab_node_to_udev_node(what);
     265           0 :         if (!node)
     266           0 :                 return log_oom();
     267             : 
     268             :         /* Nothing to apply dependencies to. */
     269           0 :         if (!is_device_path(node))
     270           0 :                 return 0;
     271             : 
     272           0 :         r = unit_name_from_path(node, ".device", &unit);
     273           0 :         if (r < 0)
     274           0 :                 return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
     275             :                                        node);
     276             : 
     277             :         /* See mount_add_default_dependencies for explanation why we create such
     278             :          * dependencies. */
     279           0 :         return write_drop_in_format(dir, unit, 50, "netdev-dependencies",
     280             :                                     "# Automatically generated by %s\n\n"
     281             :                                     "[Unit]\n"
     282             :                                     "After=" SPECIAL_NETWORK_ONLINE_TARGET " " SPECIAL_NETWORK_TARGET "\n"
     283             :                                     "Wants=" SPECIAL_NETWORK_ONLINE_TARGET "\n",
     284             :                                     program_invocation_short_name);
     285             : }
     286             : 
     287           0 : int generator_write_initrd_root_device_deps(const char *dir, const char *what) {
     288           0 :         _cleanup_free_ char *unit = NULL;
     289             :         int r;
     290             : 
     291           0 :         r = unit_name_from_path(what, ".device", &unit);
     292           0 :         if (r < 0)
     293           0 :                 return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
     294             :                                        what);
     295             : 
     296           0 :         return write_drop_in_format(dir, SPECIAL_INITRD_ROOT_DEVICE_TARGET, 50, "root-device",
     297             :                                     "# Automatically generated by %s\n\n"
     298             :                                     "[Unit]\n"
     299             :                                     "Requires=%s\n"
     300             :                                     "After=%s",
     301             :                                     program_invocation_short_name,
     302             :                                     unit,
     303             :                                     unit);
     304             : }
     305             : 
     306           0 : int generator_hook_up_mkswap(
     307             :                 const char *dir,
     308             :                 const char *what) {
     309             : 
     310           0 :         _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL;
     311           0 :         _cleanup_fclose_ FILE *f = NULL;
     312             :         const char *unit_file;
     313             :         int r;
     314             : 
     315           0 :         node = fstab_node_to_udev_node(what);
     316           0 :         if (!node)
     317           0 :                 return log_oom();
     318             : 
     319             :         /* Nothing to work on. */
     320           0 :         if (!is_device_path(node))
     321           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     322             :                                        "Cannot format something that is not a device node: %s",
     323             :                                        node);
     324             : 
     325           0 :         r = unit_name_from_path_instance("systemd-mkswap", node, ".service", &unit);
     326           0 :         if (r < 0)
     327           0 :                 return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
     328             :                                        node);
     329             : 
     330           0 :         unit_file = prefix_roota(dir, unit);
     331           0 :         log_debug("Creating %s", unit_file);
     332             : 
     333           0 :         escaped = cescape(node);
     334           0 :         if (!escaped)
     335           0 :                 return log_oom();
     336             : 
     337           0 :         r = unit_name_from_path(what, ".swap", &where_unit);
     338           0 :         if (r < 0)
     339           0 :                 return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
     340             :                                        what);
     341             : 
     342           0 :         f = fopen(unit_file, "wxe");
     343           0 :         if (!f)
     344           0 :                 return log_error_errno(errno, "Failed to create unit file %s: %m",
     345             :                                        unit_file);
     346             : 
     347           0 :         fprintf(f,
     348             :                 "# Automatically generated by %s\n\n"
     349             :                 "[Unit]\n"
     350             :                 "Description=Make Swap on %%f\n"
     351             :                 "Documentation=man:systemd-mkswap@.service(8)\n"
     352             :                 "DefaultDependencies=no\n"
     353             :                 "BindsTo=%%i.device\n"
     354             :                 "Conflicts=shutdown.target\n"
     355             :                 "After=%%i.device\n"
     356             :                 "Before=shutdown.target %s\n"
     357             :                 "\n"
     358             :                 "[Service]\n"
     359             :                 "Type=oneshot\n"
     360             :                 "RemainAfterExit=yes\n"
     361             :                 "ExecStart="SYSTEMD_MAKEFS_PATH " swap %s\n"
     362             :                 "TimeoutSec=0\n",
     363             :                 program_invocation_short_name,
     364             :                 where_unit,
     365             :                 escaped);
     366             : 
     367           0 :         r = fflush_and_check(f);
     368           0 :         if (r < 0)
     369           0 :                 return log_error_errno(r, "Failed to write unit file %s: %m", unit_file);
     370             : 
     371           0 :         return generator_add_symlink(dir, where_unit, "requires", unit);
     372             : }
     373             : 
     374           0 : int generator_hook_up_mkfs(
     375             :                 const char *dir,
     376             :                 const char *what,
     377             :                 const char *where,
     378             :                 const char *type) {
     379             : 
     380           0 :         _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL;
     381           0 :         _cleanup_fclose_ FILE *f = NULL;
     382             :         const char *unit_file;
     383             :         int r;
     384             : 
     385           0 :         node = fstab_node_to_udev_node(what);
     386           0 :         if (!node)
     387           0 :                 return log_oom();
     388             : 
     389             :         /* Nothing to work on. */
     390           0 :         if (!is_device_path(node))
     391           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     392             :                                        "Cannot format something that is not a device node: %s",
     393             :                                        node);
     394             : 
     395           0 :         if (!type || streq(type, "auto"))
     396           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     397             :                                        "Cannot format partition %s, filesystem type is not specified",
     398             :                                        node);
     399             : 
     400           0 :         r = unit_name_from_path_instance("systemd-makefs", node, ".service", &unit);
     401           0 :         if (r < 0)
     402           0 :                 return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
     403             :                                        node);
     404             : 
     405           0 :         unit_file = prefix_roota(dir, unit);
     406           0 :         log_debug("Creating %s", unit_file);
     407             : 
     408           0 :         escaped = cescape(node);
     409           0 :         if (!escaped)
     410           0 :                 return log_oom();
     411             : 
     412           0 :         r = unit_name_from_path(where, ".mount", &where_unit);
     413           0 :         if (r < 0)
     414           0 :                 return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
     415             :                                        where);
     416             : 
     417           0 :         f = fopen(unit_file, "wxe");
     418           0 :         if (!f)
     419           0 :                 return log_error_errno(errno, "Failed to create unit file %s: %m",
     420             :                                        unit_file);
     421             : 
     422           0 :         fprintf(f,
     423             :                 "# Automatically generated by %s\n\n"
     424             :                 "[Unit]\n"
     425             :                 "Description=Make File System on %%f\n"
     426             :                 "Documentation=man:systemd-makefs@.service(8)\n"
     427             :                 "DefaultDependencies=no\n"
     428             :                 "BindsTo=%%i.device\n"
     429             :                 "Conflicts=shutdown.target\n"
     430             :                 "After=%%i.device\n"
     431             :                 /* fsck might or might not be used, so let's be safe and order
     432             :                  * ourselves before both systemd-fsck@.service and the mount unit. */
     433             :                 "Before=shutdown.target systemd-fsck@%%i.service %s\n"
     434             :                 "\n"
     435             :                 "[Service]\n"
     436             :                 "Type=oneshot\n"
     437             :                 "RemainAfterExit=yes\n"
     438             :                 "ExecStart="SYSTEMD_MAKEFS_PATH " %s %s\n"
     439             :                 "TimeoutSec=0\n",
     440             :                 program_invocation_short_name,
     441             :                 where_unit,
     442             :                 type,
     443             :                 escaped);
     444             :         // XXX: what about local-fs-pre.target?
     445             : 
     446           0 :         r = fflush_and_check(f);
     447           0 :         if (r < 0)
     448           0 :                 return log_error_errno(r, "Failed to write unit file %s: %m", unit_file);
     449             : 
     450           0 :         return generator_add_symlink(dir, where_unit, "requires", unit);
     451             : }
     452             : 
     453           0 : int generator_hook_up_growfs(
     454             :                 const char *dir,
     455             :                 const char *where,
     456             :                 const char *target) {
     457             : 
     458           0 :         _cleanup_free_ char *unit = NULL, *escaped = NULL, *where_unit = NULL;
     459           0 :         _cleanup_fclose_ FILE *f = NULL;
     460             :         const char *unit_file;
     461             :         int r;
     462             : 
     463           0 :         escaped = cescape(where);
     464           0 :         if (!escaped)
     465           0 :                 return log_oom();
     466             : 
     467           0 :         r = unit_name_from_path_instance("systemd-growfs", where, ".service", &unit);
     468           0 :         if (r < 0)
     469           0 :                 return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
     470             :                                        where);
     471             : 
     472           0 :         r = unit_name_from_path(where, ".mount", &where_unit);
     473           0 :         if (r < 0)
     474           0 :                 return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
     475             :                                        where);
     476             : 
     477           0 :         unit_file = prefix_roota(dir, unit);
     478           0 :         log_debug("Creating %s", unit_file);
     479             : 
     480           0 :         f = fopen(unit_file, "wxe");
     481           0 :         if (!f)
     482           0 :                 return log_error_errno(errno, "Failed to create unit file %s: %m",
     483             :                                        unit_file);
     484             : 
     485           0 :         fprintf(f,
     486             :                 "# Automatically generated by %s\n\n"
     487             :                 "[Unit]\n"
     488             :                 "Description=Grow File System on %%f\n"
     489             :                 "Documentation=man:systemd-growfs@.service(8)\n"
     490             :                 "DefaultDependencies=no\n"
     491             :                 "BindsTo=%%i.mount\n"
     492             :                 "Conflicts=shutdown.target\n"
     493             :                 "After=%%i.mount\n"
     494             :                 "Before=shutdown.target %s\n"
     495             :                 "\n"
     496             :                 "[Service]\n"
     497             :                 "Type=oneshot\n"
     498             :                 "RemainAfterExit=yes\n"
     499             :                 "ExecStart="SYSTEMD_GROWFS_PATH " %s\n"
     500             :                 "TimeoutSec=0\n",
     501             :                 program_invocation_short_name,
     502             :                 target,
     503             :                 escaped);
     504             : 
     505           0 :         return generator_add_symlink(dir, where_unit, "wants", unit);
     506             : }
     507             : 
     508           0 : int generator_enable_remount_fs_service(const char *dir) {
     509             :         /* Pull in systemd-remount-fs.service */
     510           0 :         return generator_add_symlink(dir, SPECIAL_LOCAL_FS_TARGET, "wants",
     511             :                                      SYSTEM_DATA_UNIT_PATH "/" SPECIAL_REMOUNT_FS_SERVICE);
     512             : }
     513             : 
     514          19 : void log_setup_generator(void) {
     515          19 :         log_set_prohibit_ipc(true);
     516          19 :         log_setup_service();
     517          19 : }

Generated by: LCOV version 1.14