]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
e3631d1c LP |
2 | |
3 | #include <errno.h> | |
ede5a78f | 4 | #include <stdint.h> |
ede5a78f ST |
5 | #include <sys/ioctl.h> |
6 | #include <sys/mman.h> | |
e3631d1c LP |
7 | #include <unistd.h> |
8 | ||
ede5a78f ST |
9 | #if HAVE_XENCTRL |
10 | #define __XEN_INTERFACE_VERSION__ 0x00040900 | |
11 | #include <xen/xen.h> | |
12 | #include <xen/kexec.h> | |
13 | #include <xen/sys/privcmd.h> | |
14 | #endif | |
15 | ||
c01dcddf | 16 | #include "alloc-util.h" |
ede5a78f ST |
17 | #include "errno-util.h" |
18 | #include "fd-util.h" | |
e3631d1c LP |
19 | #include "fileio.h" |
20 | #include "log.h" | |
2bfa8466 | 21 | #include "proc-cmdline.h" |
c01dcddf | 22 | #include "raw-reboot.h" |
e3631d1c LP |
23 | #include "reboot-util.h" |
24 | #include "string-util.h" | |
25 | #include "umask-util.h" | |
c01dcddf | 26 | #include "virt.h" |
e3631d1c | 27 | |
77defcf5 | 28 | int update_reboot_parameter_and_warn(const char *parameter, bool keep) { |
e3631d1c LP |
29 | int r; |
30 | ||
31 | if (isempty(parameter)) { | |
77defcf5 VJ |
32 | if (keep) |
33 | return 0; | |
34 | ||
e3631d1c LP |
35 | if (unlink("/run/systemd/reboot-param") < 0) { |
36 | if (errno == ENOENT) | |
37 | return 0; | |
38 | ||
39 | return log_warning_errno(errno, "Failed to unlink reboot parameter file: %m"); | |
40 | } | |
41 | ||
42 | return 0; | |
43 | } | |
44 | ||
2053593f | 45 | WITH_UMASK(0022) { |
e3631d1c LP |
46 | r = write_string_file("/run/systemd/reboot-param", parameter, |
47 | WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); | |
48 | if (r < 0) | |
49 | return log_warning_errno(r, "Failed to write reboot parameter file: %m"); | |
50 | } | |
51 | ||
428b296a VJ |
52 | return 0; |
53 | } | |
54 | ||
55 | int read_reboot_parameter(char **parameter) { | |
56 | int r; | |
57 | ||
58 | assert(parameter); | |
59 | ||
60 | r = read_one_line_file("/run/systemd/reboot-param", parameter); | |
61 | if (r < 0 && r != -ENOENT) | |
62 | return log_debug_errno(r, "Failed to read /run/systemd/reboot-param: %m"); | |
63 | ||
e3631d1c LP |
64 | return 0; |
65 | } | |
c01dcddf LP |
66 | |
67 | int reboot_with_parameter(RebootFlags flags) { | |
68 | int r; | |
69 | ||
b9a1ee32 ZJS |
70 | /* Reboots the system with a parameter that is read from /run/systemd/reboot-param. Returns 0 if |
71 | * REBOOT_DRY_RUN was set and the actual reboot operation was hence skipped. If REBOOT_FALLBACK is | |
72 | * set and the reboot with parameter doesn't work out a fallback to classic reboot() is attempted. If | |
73 | * REBOOT_FALLBACK is not set, 0 is returned instead, which should be considered indication for the | |
74 | * caller to fall back to reboot() on its own, or somehow else deal with this. If REBOOT_LOG is | |
75 | * specified will log about what it is going to do, as well as all errors. */ | |
c01dcddf LP |
76 | |
77 | if (detect_container() == 0) { | |
78 | _cleanup_free_ char *parameter = NULL; | |
79 | ||
80 | r = read_one_line_file("/run/systemd/reboot-param", ¶meter); | |
81 | if (r < 0 && r != -ENOENT) | |
82 | log_full_errno(flags & REBOOT_LOG ? LOG_WARNING : LOG_DEBUG, r, | |
83 | "Failed to read reboot parameter file, ignoring: %m"); | |
84 | ||
85 | if (!isempty(parameter)) { | |
c01dcddf LP |
86 | log_full(flags & REBOOT_LOG ? LOG_INFO : LOG_DEBUG, |
87 | "Rebooting with argument '%s'.", parameter); | |
88 | ||
89 | if (flags & REBOOT_DRY_RUN) | |
90 | return 0; | |
91 | ||
92 | (void) raw_reboot(LINUX_REBOOT_CMD_RESTART2, parameter); | |
93 | ||
94 | log_full_errno(flags & REBOOT_LOG ? LOG_WARNING : LOG_DEBUG, errno, | |
95 | "Failed to reboot with parameter, retrying without: %m"); | |
96 | } | |
97 | } | |
98 | ||
99 | if (!(flags & REBOOT_FALLBACK)) | |
100 | return 0; | |
101 | ||
102 | log_full(flags & REBOOT_LOG ? LOG_INFO : LOG_DEBUG, "Rebooting."); | |
103 | ||
104 | if (flags & REBOOT_DRY_RUN) | |
105 | return 0; | |
106 | ||
107 | (void) reboot(RB_AUTOBOOT); | |
108 | ||
109 | return log_full_errno(flags & REBOOT_LOG ? LOG_ERR : LOG_DEBUG, errno, "Failed to reboot: %m"); | |
110 | } | |
2bfa8466 ZJS |
111 | |
112 | int shall_restore_state(void) { | |
113 | bool ret; | |
114 | int r; | |
115 | ||
116 | r = proc_cmdline_get_bool("systemd.restore_state", &ret); | |
117 | if (r < 0) | |
118 | return r; | |
119 | ||
120 | return r > 0 ? ret : true; | |
121 | } | |
ede5a78f ST |
122 | |
123 | static int xen_kexec_loaded(void) { | |
124 | #if HAVE_XENCTRL | |
254d1313 | 125 | _cleanup_close_ int privcmd_fd = -EBADF, buf_fd = -EBADF; |
6564918c YW |
126 | xen_kexec_status_t *buffer; |
127 | size_t size; | |
ede5a78f ST |
128 | int r; |
129 | ||
130 | if (access("/proc/xen", F_OK) < 0) { | |
131 | if (errno == ENOENT) | |
132 | return -EOPNOTSUPP; | |
133 | return log_debug_errno(errno, "Unable to test whether /proc/xen exists: %m"); | |
134 | } | |
135 | ||
136 | size = page_size(); | |
137 | if (sizeof(xen_kexec_status_t) > size) | |
138 | return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "page_size is too small for hypercall"); | |
139 | ||
140 | privcmd_fd = open("/dev/xen/privcmd", O_RDWR|O_CLOEXEC); | |
141 | if (privcmd_fd < 0) | |
142 | return log_debug_errno(errno, "Cannot access /dev/xen/privcmd: %m"); | |
143 | ||
144 | buf_fd = open("/dev/xen/hypercall", O_RDWR|O_CLOEXEC); | |
145 | if (buf_fd < 0) | |
146 | return log_debug_errno(errno, "Cannot access /dev/xen/hypercall: %m"); | |
147 | ||
148 | buffer = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, buf_fd, 0); | |
149 | if (buffer == MAP_FAILED) | |
150 | return log_debug_errno(errno, "Cannot allocate buffer for hypercall: %m"); | |
151 | ||
6564918c | 152 | *buffer = (xen_kexec_status_t) { |
ede5a78f ST |
153 | .type = KEXEC_TYPE_DEFAULT, |
154 | }; | |
155 | ||
156 | privcmd_hypercall_t call = { | |
157 | .op = __HYPERVISOR_kexec_op, | |
158 | .arg = { | |
159 | KEXEC_CMD_kexec_status, | |
160 | PTR_TO_UINT64(buffer), | |
161 | }, | |
162 | }; | |
163 | ||
164 | r = RET_NERRNO(ioctl(privcmd_fd, IOCTL_PRIVCMD_HYPERCALL, &call)); | |
165 | if (r < 0) | |
166 | log_debug_errno(r, "kexec_status failed: %m"); | |
167 | ||
168 | munmap(buffer, size); | |
169 | ||
170 | return r; | |
171 | #else | |
172 | return -EOPNOTSUPP; | |
173 | #endif | |
174 | } | |
175 | ||
176 | bool kexec_loaded(void) { | |
177 | _cleanup_free_ char *s = NULL; | |
178 | int r; | |
179 | ||
180 | r = xen_kexec_loaded(); | |
181 | if (r >= 0) | |
182 | return r; | |
183 | ||
184 | r = read_one_line_file("/sys/kernel/kexec_loaded", &s); | |
185 | if (r < 0) { | |
186 | if (r != -ENOENT) | |
187 | log_debug_errno(r, "Unable to read /sys/kernel/kexec_loaded, ignoring: %m"); | |
188 | return false; | |
189 | } | |
190 | ||
191 | return s[0] == '1'; | |
192 | } |