summaryrefslogtreecommitdiffstats
path: root/src/hibernate-resume/hibernate-resume-generator.c
blob: 01684283942f902cf074b0e0b30a8ac814d21d5b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/* SPDX-License-Identifier: LGPL-2.1-or-later */

#include "alloc-util.h"
#include "dropin.h"
#include "generator.h"
#include "hibernate-resume-config.h"
#include "initrd-util.h"
#include "log.h"
#include "main-func.h"
#include "parse-util.h"
#include "proc-cmdline.h"
#include "special.h"
#include "static-destruct.h"
#include "string-util.h"
#include "unit-name.h"

static const char *arg_dest = NULL;
static char *arg_resume_options = NULL;
static char *arg_root_options = NULL;
static bool arg_noresume = false;

STATIC_DESTRUCTOR_REGISTER(arg_resume_options, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root_options, freep);

static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
        assert(key);

        if (streq(key, "resumeflags")) {

                if (proc_cmdline_value_missing(key, value))
                        return 0;

                if (!strextend_with_separator(&arg_resume_options, ",", value))
                        return log_oom();

        } else if (streq(key, "rootflags")) {

                if (proc_cmdline_value_missing(key, value))
                        return 0;

                if (!strextend_with_separator(&arg_root_options, ",", value))
                        return log_oom();

        } else if (streq(key, "noresume")) {

                if (value) {
                        log_warning("'noresume' kernel command line option specified with an argument, ignoring.");
                        return 0;
                }

                arg_noresume = true;
        }

        return 0;
}

static int process_resume(const HibernateInfo *info) {
        _cleanup_free_ char *device_unit = NULL;
        int r;

        assert(info);

        r = unit_name_from_path(info->device, ".device", &device_unit);
        if (r < 0)
                return log_error_errno(r, "Failed to generate device unit name from path '%s': %m", info->device);

        /* If hibernate info is acquired from EFI variable, don't wait forever by default. Otherwise, if
         * swap device is not present and HibernateLocation was not correctly cleared, we end up blocking
         * the boot process infinitely. */
        r = write_drop_in_format(arg_dest, device_unit, 40, "device-timeout",
                                 "# Automatically generated by systemd-hibernate-resume-generator\n\n"
                                 "[Unit]\n"
                                 "JobTimeoutSec=%s\n",
                                 info->cmdline ? "infinity" : "2min");
        if (r < 0)
                log_warning_errno(r, "Failed to write device timeout drop-in, ignoring: %m");

        r = generator_write_timeouts(arg_dest,
                                     info->device,
                                     info->device,
                                     arg_resume_options ?: arg_root_options,
                                     NULL);
        if (r < 0)
                log_warning_errno(r, "Failed to write device timeout drop-in, ignoring: %m");

        r = write_drop_in_format(arg_dest, SPECIAL_HIBERNATE_RESUME_SERVICE, 90, "device-dependency",
                                 "# Automatically generated by systemd-hibernate-resume-generator\n\n"
                                 "[Unit]\n"
                                 "BindsTo=%1$s\n"
                                 "After=%1$s\n",
                                 device_unit);
        if (r < 0)
                return log_error_errno(r, "Failed to write device dependency drop-in: %m");

        return generator_add_symlink(arg_dest, SPECIAL_SYSINIT_TARGET, "wants", SPECIAL_HIBERNATE_RESUME_SERVICE);
}

static int run(const char *dest, const char *dest_early, const char *dest_late) {
        _cleanup_(hibernate_info_done) HibernateInfo info = {};
        int r;

        arg_dest = ASSERT_PTR(dest);

        /* Don't even consider resuming outside of initrd. */
        if (!in_initrd()) {
                log_debug("Not running in initrd, exiting.");
                return 0;
        }

        r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
        if (r < 0)
                log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");

        if (arg_noresume) {
                log_info("Found 'noresume' on the kernel command line, exiting.");
                return 0;
        }

        r = acquire_hibernate_info(&info);
        if (r == -ENODEV) {
                log_debug_errno(r, "No resume device found, exiting.");
                return 0;
        }
        if (r < 0)
                return r;

        return process_resume(&info);
}

DEFINE_MAIN_GENERATOR_FUNCTION(run);