LCOV - code coverage report
Current view: top level - shutdown - shutdown.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 0 283 0.0 %
Date: 2019-08-23 13:36:53 Functions: 0 7 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 334 0.0 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : /***
       3                 :            :   Copyright © 2010 ProFUSION embedded systems
       4                 :            : ***/
       5                 :            : 
       6                 :            : #include <errno.h>
       7                 :            : #include <getopt.h>
       8                 :            : #include <linux/reboot.h>
       9                 :            : #include <signal.h>
      10                 :            : #include <stdbool.h>
      11                 :            : #include <stdlib.h>
      12                 :            : #include <sys/mman.h>
      13                 :            : #include <sys/mount.h>
      14                 :            : #include <sys/reboot.h>
      15                 :            : #include <sys/stat.h>
      16                 :            : #include <unistd.h>
      17                 :            : 
      18                 :            : #include "alloc-util.h"
      19                 :            : #include "async.h"
      20                 :            : #include "cgroup-util.h"
      21                 :            : #include "def.h"
      22                 :            : #include "exec-util.h"
      23                 :            : #include "fd-util.h"
      24                 :            : #include "fileio.h"
      25                 :            : #include "killall.h"
      26                 :            : #include "log.h"
      27                 :            : #include "missing.h"
      28                 :            : #include "parse-util.h"
      29                 :            : #include "process-util.h"
      30                 :            : #include "reboot-util.h"
      31                 :            : #include "rlimit-util.h"
      32                 :            : #include "signal-util.h"
      33                 :            : #include "string-util.h"
      34                 :            : #include "switch-root.h"
      35                 :            : #include "sysctl-util.h"
      36                 :            : #include "terminal-util.h"
      37                 :            : #include "umount.h"
      38                 :            : #include "util.h"
      39                 :            : #include "virt.h"
      40                 :            : #include "watchdog.h"
      41                 :            : 
      42                 :            : #define SYNC_PROGRESS_ATTEMPTS 3
      43                 :            : #define SYNC_TIMEOUT_USEC (10*USEC_PER_SEC)
      44                 :            : 
      45                 :            : static char* arg_verb;
      46                 :            : static uint8_t arg_exit_code;
      47                 :            : static usec_t arg_timeout = DEFAULT_TIMEOUT_USEC;
      48                 :            : 
      49                 :          0 : static int parse_argv(int argc, char *argv[]) {
      50                 :            :         enum {
      51                 :            :                 ARG_LOG_LEVEL = 0x100,
      52                 :            :                 ARG_LOG_TARGET,
      53                 :            :                 ARG_LOG_COLOR,
      54                 :            :                 ARG_LOG_LOCATION,
      55                 :            :                 ARG_EXIT_CODE,
      56                 :            :                 ARG_TIMEOUT,
      57                 :            :         };
      58                 :            : 
      59                 :            :         static const struct option options[] = {
      60                 :            :                 { "log-level",     required_argument, NULL, ARG_LOG_LEVEL    },
      61                 :            :                 { "log-target",    required_argument, NULL, ARG_LOG_TARGET   },
      62                 :            :                 { "log-color",     optional_argument, NULL, ARG_LOG_COLOR    },
      63                 :            :                 { "log-location",  optional_argument, NULL, ARG_LOG_LOCATION },
      64                 :            :                 { "exit-code",     required_argument, NULL, ARG_EXIT_CODE    },
      65                 :            :                 { "timeout",       required_argument, NULL, ARG_TIMEOUT      },
      66                 :            :                 {}
      67                 :            :         };
      68                 :            : 
      69                 :            :         int c, r;
      70                 :            : 
      71         [ #  # ]:          0 :         assert(argc >= 1);
      72         [ #  # ]:          0 :         assert(argv);
      73                 :            : 
      74                 :            :         /* "-" prevents getopt from permuting argv[] and moving the verb away
      75                 :            :          * from argv[1]. Our interface to initrd promises it'll be there. */
      76         [ #  # ]:          0 :         while ((c = getopt_long(argc, argv, "-", options, NULL)) >= 0)
      77   [ #  #  #  #  :          0 :                 switch (c) {
             #  #  #  #  
                      # ]
      78                 :            : 
      79                 :          0 :                 case ARG_LOG_LEVEL:
      80                 :          0 :                         r = log_set_max_level_from_string(optarg);
      81         [ #  # ]:          0 :                         if (r < 0)
      82         [ #  # ]:          0 :                                 log_error_errno(r, "Failed to parse log level %s, ignoring: %m", optarg);
      83                 :            : 
      84                 :          0 :                         break;
      85                 :            : 
      86                 :          0 :                 case ARG_LOG_TARGET:
      87                 :          0 :                         r = log_set_target_from_string(optarg);
      88         [ #  # ]:          0 :                         if (r < 0)
      89         [ #  # ]:          0 :                                 log_error_errno(r, "Failed to parse log target %s, ignoring: %m", optarg);
      90                 :            : 
      91                 :          0 :                         break;
      92                 :            : 
      93                 :          0 :                 case ARG_LOG_COLOR:
      94                 :            : 
      95         [ #  # ]:          0 :                         if (optarg) {
      96                 :          0 :                                 r = log_show_color_from_string(optarg);
      97         [ #  # ]:          0 :                                 if (r < 0)
      98         [ #  # ]:          0 :                                         log_error_errno(r, "Failed to parse log color setting %s, ignoring: %m", optarg);
      99                 :            :                         } else
     100                 :          0 :                                 log_show_color(true);
     101                 :            : 
     102                 :          0 :                         break;
     103                 :            : 
     104                 :          0 :                 case ARG_LOG_LOCATION:
     105         [ #  # ]:          0 :                         if (optarg) {
     106                 :          0 :                                 r = log_show_location_from_string(optarg);
     107         [ #  # ]:          0 :                                 if (r < 0)
     108         [ #  # ]:          0 :                                         log_error_errno(r, "Failed to parse log location setting %s, ignoring: %m", optarg);
     109                 :            :                         } else
     110                 :          0 :                                 log_show_location(true);
     111                 :            : 
     112                 :          0 :                         break;
     113                 :            : 
     114                 :          0 :                 case ARG_EXIT_CODE:
     115                 :          0 :                         r = safe_atou8(optarg, &arg_exit_code);
     116         [ #  # ]:          0 :                         if (r < 0)
     117         [ #  # ]:          0 :                                 log_error_errno(r, "Failed to parse exit code %s, ignoring: %m", optarg);
     118                 :            : 
     119                 :          0 :                         break;
     120                 :            : 
     121                 :          0 :                 case ARG_TIMEOUT:
     122                 :          0 :                         r = parse_sec(optarg, &arg_timeout);
     123         [ #  # ]:          0 :                         if (r < 0)
     124         [ #  # ]:          0 :                                 log_error_errno(r, "Failed to parse shutdown timeout %s, ignoring: %m", optarg);
     125                 :            : 
     126                 :          0 :                         break;
     127                 :            : 
     128                 :          0 :                 case '\001':
     129         [ #  # ]:          0 :                         if (!arg_verb)
     130                 :          0 :                                 arg_verb = optarg;
     131                 :            :                         else
     132         [ #  # ]:          0 :                                 log_error("Excess arguments, ignoring");
     133                 :          0 :                         break;
     134                 :            : 
     135                 :          0 :                 case '?':
     136                 :          0 :                         return -EINVAL;
     137                 :            : 
     138                 :          0 :                 default:
     139                 :          0 :                         assert_not_reached("Unhandled option code.");
     140                 :            :                 }
     141                 :            : 
     142         [ #  # ]:          0 :         if (!arg_verb)
     143         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     144                 :            :                                        "Verb argument missing.");
     145                 :            : 
     146                 :          0 :         return 0;
     147                 :            : }
     148                 :            : 
     149                 :          0 : static int switch_root_initramfs(void) {
     150         [ #  # ]:          0 :         if (mount("/run/initramfs", "/run/initramfs", NULL, MS_BIND, NULL) < 0)
     151         [ #  # ]:          0 :                 return log_error_errno(errno, "Failed to mount bind /run/initramfs on /run/initramfs: %m");
     152                 :            : 
     153         [ #  # ]:          0 :         if (mount(NULL, "/run/initramfs", NULL, MS_PRIVATE, NULL) < 0)
     154         [ #  # ]:          0 :                 return log_error_errno(errno, "Failed to make /run/initramfs private mount: %m");
     155                 :            : 
     156                 :            :         /* switch_root with MS_BIND, because there might still be processes lurking around, which have open file descriptors.
     157                 :            :          * /run/initramfs/shutdown will take care of these.
     158                 :            :          * Also do not detach the old root, because /run/initramfs/shutdown needs to access it.
     159                 :            :          */
     160                 :          0 :         return switch_root("/run/initramfs", "/oldroot", false, MS_BIND);
     161                 :            : }
     162                 :            : 
     163                 :            : /* Read the following fields from /proc/meminfo:
     164                 :            :  *
     165                 :            :  *  NFS_Unstable
     166                 :            :  *  Writeback
     167                 :            :  *  Dirty
     168                 :            :  *
     169                 :            :  * Return true if the sum of these fields is greater than the previous
     170                 :            :  * value input. For all other issues, report the failure and indicate that
     171                 :            :  * the sync is not making progress.
     172                 :            :  */
     173                 :          0 : static int sync_making_progress(unsigned long long *prev_dirty) {
     174                 :          0 :         _cleanup_fclose_ FILE *f = NULL;
     175                 :          0 :         unsigned long long val = 0;
     176                 :            :         int ret;
     177                 :            : 
     178                 :          0 :         f = fopen("/proc/meminfo", "re");
     179         [ #  # ]:          0 :         if (!f)
     180         [ #  # ]:          0 :                 return log_warning_errno(errno, "Failed to open /proc/meminfo: %m");
     181                 :            : 
     182                 :          0 :         for (;;) {
     183   [ #  #  #  # ]:          0 :                 _cleanup_free_ char *line = NULL;
     184                 :          0 :                 unsigned long long ull = 0;
     185                 :            :                 int q;
     186                 :            : 
     187                 :          0 :                 q = read_line(f, LONG_LINE_MAX, &line);
     188         [ #  # ]:          0 :                 if (q < 0)
     189         [ #  # ]:          0 :                         return log_warning_errno(q, "Failed to parse /proc/meminfo: %m");
     190         [ #  # ]:          0 :                 if (q == 0)
     191                 :          0 :                         break;
     192                 :            : 
     193   [ #  #  #  #  :          0 :                 if (!first_word(line, "NFS_Unstable:") && !first_word(line, "Writeback:") && !first_word(line, "Dirty:"))
                   #  # ]
     194                 :          0 :                         continue;
     195                 :            : 
     196                 :          0 :                 errno = 0;
     197         [ #  # ]:          0 :                 if (sscanf(line, "%*s %llu %*s", &ull) != 1) {
     198         [ #  # ]:          0 :                         if (errno != 0)
     199         [ #  # ]:          0 :                                 log_warning_errno(errno, "Failed to parse /proc/meminfo: %m");
     200                 :            :                         else
     201         [ #  # ]:          0 :                                 log_warning("Failed to parse /proc/meminfo");
     202                 :            : 
     203                 :          0 :                         return false;
     204                 :            :                 }
     205                 :            : 
     206                 :          0 :                 val += ull;
     207                 :            :         }
     208                 :            : 
     209                 :          0 :         ret = *prev_dirty > val;
     210                 :          0 :         *prev_dirty = val;
     211                 :          0 :         return ret;
     212                 :            : }
     213                 :            : 
     214                 :          0 : static void sync_with_progress(void) {
     215                 :          0 :         unsigned long long dirty = ULLONG_MAX;
     216                 :            :         unsigned checks;
     217                 :            :         pid_t pid;
     218                 :            :         int r;
     219                 :            : 
     220   [ #  #  #  # ]:          0 :         BLOCK_SIGNALS(SIGCHLD);
     221                 :            : 
     222                 :            :         /* Due to the possibility of the sync operation hanging, we fork a child process and monitor the progress. If
     223                 :            :          * the timeout lapses, the assumption is that that particular sync stalled. */
     224                 :            : 
     225                 :          0 :         r = asynchronous_sync(&pid);
     226         [ #  # ]:          0 :         if (r < 0) {
     227         [ #  # ]:          0 :                 log_error_errno(r, "Failed to fork sync(): %m");
     228                 :          0 :                 return;
     229                 :            :         }
     230                 :            : 
     231         [ #  # ]:          0 :         log_info("Syncing filesystems and block devices.");
     232                 :            : 
     233                 :            :         /* Start monitoring the sync operation. If more than
     234                 :            :          * SYNC_PROGRESS_ATTEMPTS lapse without progress being made,
     235                 :            :          * we assume that the sync is stalled */
     236         [ #  # ]:          0 :         for (checks = 0; checks < SYNC_PROGRESS_ATTEMPTS; checks++) {
     237                 :          0 :                 r = wait_for_terminate_with_timeout(pid, SYNC_TIMEOUT_USEC);
     238         [ #  # ]:          0 :                 if (r == 0)
     239                 :            :                         /* Sync finished without error.
     240                 :            :                          * (The sync itself does not return an error code) */
     241                 :          0 :                         return;
     242         [ #  # ]:          0 :                 else if (r == -ETIMEDOUT) {
     243                 :            :                         /* Reset the check counter if the "Dirty" value is
     244                 :            :                          * decreasing */
     245         [ #  # ]:          0 :                         if (sync_making_progress(&dirty) > 0)
     246                 :          0 :                                 checks = 0;
     247                 :            :                 } else {
     248         [ #  # ]:          0 :                         log_error_errno(r, "Failed to sync filesystems and block devices: %m");
     249                 :          0 :                         return;
     250                 :            :                 }
     251                 :            :         }
     252                 :            : 
     253                 :            :         /* Only reached in the event of a timeout. We should issue a kill
     254                 :            :          * to the stray process. */
     255         [ #  # ]:          0 :         log_error("Syncing filesystems and block devices - timed out, issuing SIGKILL to PID "PID_FMT".", pid);
     256                 :          0 :         (void) kill(pid, SIGKILL);
     257                 :            : }
     258                 :            : 
     259                 :          0 : static int read_current_sysctl_printk_log_level(void) {
     260                 :          0 :         _cleanup_free_ char *sysctl_printk_vals = NULL, *sysctl_printk_curr = NULL;
     261                 :            :         int current_lvl;
     262                 :            :         const char *p;
     263                 :            :         int r;
     264                 :            : 
     265                 :          0 :         r = sysctl_read("kernel/printk", &sysctl_printk_vals);
     266         [ #  # ]:          0 :         if (r < 0)
     267         [ #  # ]:          0 :                 return log_debug_errno(r, "Cannot read sysctl kernel.printk: %m");
     268                 :            : 
     269                 :          0 :         p = sysctl_printk_vals;
     270                 :          0 :         r = extract_first_word(&p, &sysctl_printk_curr, NULL, 0);
     271         [ #  # ]:          0 :         if (r < 0)
     272         [ #  # ]:          0 :                 return log_debug_errno(r, "Failed to split out kernel printk priority: %m");
     273         [ #  # ]:          0 :         if (r == 0)
     274         [ #  # ]:          0 :                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Short read while reading kernel.printk sysctl");
     275                 :            : 
     276                 :          0 :         r = safe_atoi(sysctl_printk_curr, &current_lvl);
     277         [ #  # ]:          0 :         if (r < 0)
     278         [ #  # ]:          0 :                 return log_debug_errno(r, "Failed to parse kernel.printk sysctl: %s", sysctl_printk_vals);
     279                 :            : 
     280                 :          0 :         return current_lvl;
     281                 :            : }
     282                 :            : 
     283                 :          0 : static void bump_sysctl_printk_log_level(int min_level) {
     284                 :            :         int current_lvl, r;
     285                 :            : 
     286                 :            :         /* Set the logging level to be able to see messages with log level smaller or equal to min_level */
     287                 :            : 
     288                 :          0 :         current_lvl = read_current_sysctl_printk_log_level();
     289   [ #  #  #  # ]:          0 :         if (current_lvl < 0 || current_lvl >= min_level + 1)
     290                 :          0 :                 return;
     291                 :            : 
     292                 :          0 :         r = sysctl_writef("kernel/printk", "%i", min_level + 1);
     293         [ #  # ]:          0 :         if (r < 0)
     294         [ #  # ]:          0 :                 log_debug_errno(r, "Failed to bump kernel.printk to %i: %m", min_level + 1);
     295                 :            : }
     296                 :            : 
     297                 :          0 : int main(int argc, char *argv[]) {
     298                 :          0 :         bool need_umount, need_swapoff, need_loop_detach, need_dm_detach, in_container, use_watchdog = false, can_initrd;
     299                 :          0 :         _cleanup_free_ char *cgroup = NULL;
     300                 :            :         char *arguments[3], *watchdog_device;
     301                 :          0 :         int cmd, r, umount_log_level = LOG_INFO;
     302                 :            :         static const char* const dirs[] = {SYSTEM_SHUTDOWN_PATH, NULL};
     303                 :            : 
     304                 :            :         /* The log target defaults to console, but the original systemd process will pass its log target in through a
     305                 :            :          * command line argument, which will override this default. Also, ensure we'll never log to the journal or
     306                 :            :          * syslog, as these logging daemons are either already dead or will die very soon. */
     307                 :            : 
     308                 :          0 :         log_set_target(LOG_TARGET_CONSOLE);
     309                 :          0 :         log_set_prohibit_ipc(true);
     310                 :          0 :         log_parse_environment();
     311                 :            : 
     312                 :          0 :         r = parse_argv(argc, argv);
     313         [ #  # ]:          0 :         if (r < 0)
     314                 :          0 :                 goto error;
     315                 :            : 
     316                 :          0 :         log_open();
     317                 :            : 
     318                 :          0 :         umask(0022);
     319                 :            : 
     320         [ #  # ]:          0 :         if (getpid_cached() != 1) {
     321         [ #  # ]:          0 :                 log_error("Not executed by init (PID 1).");
     322                 :          0 :                 r = -EPERM;
     323                 :          0 :                 goto error;
     324                 :            :         }
     325                 :            : 
     326         [ #  # ]:          0 :         if (streq(arg_verb, "reboot"))
     327                 :          0 :                 cmd = RB_AUTOBOOT;
     328         [ #  # ]:          0 :         else if (streq(arg_verb, "poweroff"))
     329                 :          0 :                 cmd = RB_POWER_OFF;
     330         [ #  # ]:          0 :         else if (streq(arg_verb, "halt"))
     331                 :          0 :                 cmd = RB_HALT_SYSTEM;
     332         [ #  # ]:          0 :         else if (streq(arg_verb, "kexec"))
     333                 :          0 :                 cmd = LINUX_REBOOT_CMD_KEXEC;
     334         [ #  # ]:          0 :         else if (streq(arg_verb, "exit"))
     335                 :          0 :                 cmd = 0; /* ignored, just checking that arg_verb is valid */
     336                 :            :         else {
     337         [ #  # ]:          0 :                 log_error("Unknown action '%s'.", arg_verb);
     338                 :          0 :                 r = -EINVAL;
     339                 :          0 :                 goto error;
     340                 :            :         }
     341                 :            : 
     342                 :          0 :         (void) cg_get_root_path(&cgroup);
     343                 :          0 :         in_container = detect_container() > 0;
     344                 :            : 
     345                 :            :         /* If the logging messages are going to KMSG, and if we are not running from a container, then try to
     346                 :            :          * update the sysctl kernel.printk current value in order to see "info" messages; This current log
     347                 :            :          * level is not updated if already big enough.
     348                 :            :          */
     349         [ #  # ]:          0 :         if (!in_container &&
     350   [ #  #  #  # ]:          0 :             IN_SET(log_get_target(),
     351                 :            :                    LOG_TARGET_AUTO,
     352                 :            :                    LOG_TARGET_JOURNAL_OR_KMSG,
     353                 :            :                    LOG_TARGET_SYSLOG_OR_KMSG,
     354                 :            :                    LOG_TARGET_KMSG))
     355                 :          0 :                 bump_sysctl_printk_log_level(LOG_WARNING);
     356                 :            : 
     357                 :          0 :         use_watchdog = getenv("WATCHDOG_USEC");
     358                 :          0 :         watchdog_device = getenv("WATCHDOG_DEVICE");
     359         [ #  # ]:          0 :         if (watchdog_device) {
     360                 :          0 :                 r = watchdog_set_device(watchdog_device);
     361         [ #  # ]:          0 :                 if (r < 0)
     362         [ #  # ]:          0 :                         log_warning_errno(r, "Failed to set watchdog device to %s, ignoring: %m",
     363                 :            :                                           watchdog_device);
     364                 :            :         }
     365                 :            : 
     366                 :            :         /* Lock us into memory */
     367                 :          0 :         (void) mlockall(MCL_CURRENT|MCL_FUTURE);
     368                 :            : 
     369                 :            :         /* Synchronize everything that is not written to disk yet at this point already. This is a good idea so that
     370                 :            :          * slow IO is processed here already and the final process killing spree is not impacted by processes
     371                 :            :          * desperately trying to sync IO to disk within their timeout. Do not remove this sync, data corruption will
     372                 :            :          * result. */
     373         [ #  # ]:          0 :         if (!in_container)
     374                 :          0 :                 sync_with_progress();
     375                 :            : 
     376                 :          0 :         disable_coredumps();
     377                 :            : 
     378         [ #  # ]:          0 :         log_info("Sending SIGTERM to remaining processes...");
     379                 :          0 :         broadcast_signal(SIGTERM, true, true, arg_timeout);
     380                 :            : 
     381         [ #  # ]:          0 :         log_info("Sending SIGKILL to remaining processes...");
     382                 :          0 :         broadcast_signal(SIGKILL, true, false, arg_timeout);
     383                 :            : 
     384                 :          0 :         need_umount = !in_container;
     385                 :          0 :         need_swapoff = !in_container;
     386                 :          0 :         need_loop_detach = !in_container;
     387                 :          0 :         need_dm_detach = !in_container;
     388   [ #  #  #  #  :          0 :         can_initrd = !in_container && !in_initrd() && access("/run/initramfs/shutdown", X_OK) == 0;
                   #  # ]
     389                 :            : 
     390                 :            :         /* Unmount all mountpoints, swaps, and loopback devices */
     391                 :          0 :         for (;;) {
     392                 :          0 :                 bool changed = false;
     393                 :            : 
     394         [ #  # ]:          0 :                 if (use_watchdog)
     395                 :          0 :                         (void) watchdog_ping();
     396                 :            : 
     397                 :            :                 /* Let's trim the cgroup tree on each iteration so
     398                 :            :                    that we leave an empty cgroup tree around, so that
     399                 :            :                    container managers get a nice notify event when we
     400                 :            :                    are down */
     401         [ #  # ]:          0 :                 if (cgroup)
     402                 :          0 :                         (void) cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false);
     403                 :            : 
     404         [ #  # ]:          0 :                 if (need_umount) {
     405         [ #  # ]:          0 :                         log_info("Unmounting file systems.");
     406                 :          0 :                         r = umount_all(&changed, umount_log_level);
     407         [ #  # ]:          0 :                         if (r == 0) {
     408                 :          0 :                                 need_umount = false;
     409         [ #  # ]:          0 :                                 log_info("All filesystems unmounted.");
     410         [ #  # ]:          0 :                         } else if (r > 0)
     411         [ #  # ]:          0 :                                 log_info("Not all file systems unmounted, %d left.", r);
     412                 :            :                         else
     413         [ #  # ]:          0 :                                 log_error_errno(r, "Failed to unmount file systems: %m");
     414                 :            :                 }
     415                 :            : 
     416         [ #  # ]:          0 :                 if (need_swapoff) {
     417         [ #  # ]:          0 :                         log_info("Deactivating swaps.");
     418                 :          0 :                         r = swapoff_all(&changed);
     419         [ #  # ]:          0 :                         if (r == 0) {
     420                 :          0 :                                 need_swapoff = false;
     421         [ #  # ]:          0 :                                 log_info("All swaps deactivated.");
     422         [ #  # ]:          0 :                         } else if (r > 0)
     423         [ #  # ]:          0 :                                 log_info("Not all swaps deactivated, %d left.", r);
     424                 :            :                         else
     425         [ #  # ]:          0 :                                 log_error_errno(r, "Failed to deactivate swaps: %m");
     426                 :            :                 }
     427                 :            : 
     428         [ #  # ]:          0 :                 if (need_loop_detach) {
     429         [ #  # ]:          0 :                         log_info("Detaching loop devices.");
     430                 :          0 :                         r = loopback_detach_all(&changed, umount_log_level);
     431         [ #  # ]:          0 :                         if (r == 0) {
     432                 :          0 :                                 need_loop_detach = false;
     433         [ #  # ]:          0 :                                 log_info("All loop devices detached.");
     434         [ #  # ]:          0 :                         } else if (r > 0)
     435         [ #  # ]:          0 :                                 log_info("Not all loop devices detached, %d left.", r);
     436                 :            :                         else
     437         [ #  # ]:          0 :                                 log_error_errno(r, "Failed to detach loop devices: %m");
     438                 :            :                 }
     439                 :            : 
     440         [ #  # ]:          0 :                 if (need_dm_detach) {
     441         [ #  # ]:          0 :                         log_info("Detaching DM devices.");
     442                 :          0 :                         r = dm_detach_all(&changed, umount_log_level);
     443         [ #  # ]:          0 :                         if (r == 0) {
     444                 :          0 :                                 need_dm_detach = false;
     445         [ #  # ]:          0 :                                 log_info("All DM devices detached.");
     446         [ #  # ]:          0 :                         } else if (r > 0)
     447         [ #  # ]:          0 :                                 log_info("Not all DM devices detached, %d left.", r);
     448                 :            :                         else
     449         [ #  # ]:          0 :                                 log_error_errno(r, "Failed to detach DM devices: %m");
     450                 :            :                 }
     451                 :            : 
     452   [ #  #  #  #  :          0 :                 if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
             #  #  #  # ]
     453         [ #  # ]:          0 :                         log_info("All filesystems, swaps, loop devices and DM devices detached.");
     454                 :            :                         /* Yay, done */
     455                 :          0 :                         break;
     456                 :            :                 }
     457                 :            : 
     458   [ #  #  #  #  :          0 :                 if (!changed && umount_log_level == LOG_INFO && !can_initrd) {
                   #  # ]
     459                 :            :                         /* There are things we cannot get rid of. Loop one more time
     460                 :            :                          * with LOG_ERR to inform the user. Note that we don't need
     461                 :            :                          * to do this if there is a initrd to switch to, because that
     462                 :            :                          * one is likely to get rid of the remounting mounts. If not,
     463                 :            :                          * it will log about them. */
     464                 :          0 :                         umount_log_level = LOG_ERR;
     465                 :          0 :                         continue;
     466                 :            :                 }
     467                 :            : 
     468                 :            :                 /* If in this iteration we didn't manage to
     469                 :            :                  * unmount/deactivate anything, we simply give up */
     470         [ #  # ]:          0 :                 if (!changed) {
     471   [ #  #  #  #  :          0 :                         log_info("Cannot finalize remaining%s%s%s%s continuing.",
          #  #  #  #  #  
                      # ]
     472                 :            :                                  need_umount ? " file systems," : "",
     473                 :            :                                  need_swapoff ? " swap devices," : "",
     474                 :            :                                  need_loop_detach ? " loop devices," : "",
     475                 :            :                                  need_dm_detach ? " DM devices," : "");
     476                 :          0 :                         break;
     477                 :            :                 }
     478                 :            : 
     479   [ #  #  #  #  :          0 :                 log_debug("Couldn't finalize remaining %s%s%s%s trying again.",
          #  #  #  #  #  
                      # ]
     480                 :            :                           need_umount ? " file systems," : "",
     481                 :            :                           need_swapoff ? " swap devices," : "",
     482                 :            :                           need_loop_detach ? " loop devices," : "",
     483                 :            :                           need_dm_detach ? " DM devices," : "");
     484                 :            :         }
     485                 :            : 
     486                 :            :         /* We're done with the watchdog. */
     487                 :          0 :         watchdog_free_device();
     488                 :            : 
     489                 :          0 :         arguments[0] = NULL;
     490                 :          0 :         arguments[1] = arg_verb;
     491                 :          0 :         arguments[2] = NULL;
     492                 :          0 :         (void) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
     493                 :            : 
     494                 :          0 :         (void) rlimit_nofile_safe();
     495                 :            : 
     496         [ #  # ]:          0 :         if (can_initrd) {
     497                 :          0 :                 r = switch_root_initramfs();
     498         [ #  # ]:          0 :                 if (r >= 0) {
     499                 :          0 :                         argv[0] = (char*) "/shutdown";
     500                 :            : 
     501                 :          0 :                         (void) setsid();
     502                 :          0 :                         (void) make_console_stdio();
     503                 :            : 
     504         [ #  # ]:          0 :                         log_info("Successfully changed into root pivot.\n"
     505                 :            :                                  "Returning to initrd...");
     506                 :            : 
     507                 :          0 :                         execv("/shutdown", argv);
     508         [ #  # ]:          0 :                         log_error_errno(errno, "Failed to execute shutdown binary: %m");
     509                 :            :                 } else
     510         [ #  # ]:          0 :                         log_error_errno(r, "Failed to switch root to \"/run/initramfs\": %m");
     511                 :            :         }
     512                 :            : 
     513   [ #  #  #  #  :          0 :         if (need_umount || need_swapoff || need_loop_detach || need_dm_detach)
             #  #  #  # ]
     514   [ #  #  #  #  :          0 :                 log_error("Failed to finalize %s%s%s%s ignoring",
          #  #  #  #  #  
                      # ]
     515                 :            :                           need_umount ? " file systems," : "",
     516                 :            :                           need_swapoff ? " swap devices," : "",
     517                 :            :                           need_loop_detach ? " loop devices," : "",
     518                 :            :                           need_dm_detach ? " DM devices," : "");
     519                 :            : 
     520                 :            :         /* The kernel will automatically flush ATA disks and suchlike on reboot(), but the file systems need to be
     521                 :            :          * sync'ed explicitly in advance. So let's do this here, but not needlessly slow down containers. Note that we
     522                 :            :          * sync'ed things already once above, but we did some more work since then which might have caused IO, hence
     523                 :            :          * let's do it once more. Do not remove this sync, data corruption will result. */
     524         [ #  # ]:          0 :         if (!in_container)
     525                 :          0 :                 sync_with_progress();
     526                 :            : 
     527         [ #  # ]:          0 :         if (streq(arg_verb, "exit")) {
     528         [ #  # ]:          0 :                 if (in_container)
     529                 :          0 :                         return arg_exit_code;
     530                 :            : 
     531                 :          0 :                 cmd = RB_POWER_OFF; /* We cannot exit() on the host, fallback on another method. */
     532                 :            :         }
     533                 :            : 
     534   [ #  #  #  #  :          0 :         switch (cmd) {
                      # ]
     535                 :            : 
     536                 :          0 :         case LINUX_REBOOT_CMD_KEXEC:
     537                 :            : 
     538         [ #  # ]:          0 :                 if (!in_container) {
     539                 :            :                         /* We cheat and exec kexec to avoid doing all its work */
     540         [ #  # ]:          0 :                         log_info("Rebooting with kexec.");
     541                 :            : 
     542                 :          0 :                         r = safe_fork("(sd-kexec)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_WAIT, NULL);
     543         [ #  # ]:          0 :                         if (r == 0) {
     544                 :          0 :                                 const char * const args[] = {
     545                 :            :                                         KEXEC, "-e", NULL
     546                 :            :                                 };
     547                 :            : 
     548                 :            :                                 /* Child */
     549                 :            : 
     550                 :          0 :                                 execv(args[0], (char * const *) args);
     551                 :          0 :                                 _exit(EXIT_FAILURE);
     552                 :            :                         }
     553                 :            : 
     554                 :            :                         /* If we are still running, then the kexec can't have worked, let's fall through */
     555                 :            :                 }
     556                 :            : 
     557                 :          0 :                 cmd = RB_AUTOBOOT;
     558                 :            :                 _fallthrough_;
     559                 :            : 
     560                 :          0 :         case RB_AUTOBOOT:
     561                 :          0 :                 (void) reboot_with_parameter(REBOOT_LOG);
     562         [ #  # ]:          0 :                 log_info("Rebooting.");
     563                 :          0 :                 break;
     564                 :            : 
     565                 :          0 :         case RB_POWER_OFF:
     566         [ #  # ]:          0 :                 log_info("Powering off.");
     567                 :          0 :                 break;
     568                 :            : 
     569                 :          0 :         case RB_HALT_SYSTEM:
     570         [ #  # ]:          0 :                 log_info("Halting system.");
     571                 :          0 :                 break;
     572                 :            : 
     573                 :          0 :         default:
     574                 :          0 :                 assert_not_reached("Unknown magic");
     575                 :            :         }
     576                 :            : 
     577                 :          0 :         (void) reboot(cmd);
     578   [ #  #  #  # ]:          0 :         if (errno == EPERM && in_container) {
     579                 :            :                 /* If we are in a container, and we lacked
     580                 :            :                  * CAP_SYS_BOOT just exit, this will kill our
     581                 :            :                  * container for good. */
     582         [ #  # ]:          0 :                 log_info("Exiting container.");
     583                 :          0 :                 return EXIT_SUCCESS;
     584                 :            :         }
     585                 :            : 
     586         [ #  # ]:          0 :         r = log_error_errno(errno, "Failed to invoke reboot(): %m");
     587                 :            : 
     588                 :          0 :   error:
     589         [ #  # ]:          0 :         log_emergency_errno(r, "Critical error while doing system shutdown: %m");
     590                 :          0 :         freeze();
     591                 :            : }

Generated by: LCOV version 1.14