assert(m);
assert(!IN_SET(handle, HANDLE_IGNORE, HANDLE_LOCK, HANDLE_SLEEP));
- if (handle == HANDLE_KEXEC && access(KEXEC, X_OK) < 0)
- return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
- "Requested %s operation not supported, ignoring.", handle_action_to_string(handle));
-
if (m->delayed_action)
return log_debug_errno(SYNTHETIC_ERRNO(EALREADY),
"Action %s already in progress, ignoring requested %s operation.",
return (cached = b);
}
-static int xen_kexec_loaded(void) {
#if HAVE_XENCTRL
+static int xen_kexec_command(uint64_t cmd) {
_cleanup_close_ int privcmd_fd = -EBADF, buf_fd = -EBADF;
- xen_kexec_status_t *buffer;
+ void *buffer;
size_t size;
int r;
+ assert(IN_SET(cmd, KEXEC_CMD_kexec, KEXEC_CMD_kexec_status));
+
if (access("/proc/xen", F_OK) < 0) {
if (errno == ENOENT)
return -EOPNOTSUPP;
}
size = page_size();
- if (sizeof(xen_kexec_status_t) > size)
+ if ((cmd == KEXEC_CMD_kexec_status && sizeof(xen_kexec_status_t) > size) ||
+ (cmd == KEXEC_CMD_kexec && sizeof(xen_kexec_exec_t) > size))
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "page_size is too small for hypercall");
privcmd_fd = open("/dev/xen/privcmd", O_RDWR|O_CLOEXEC);
if (buffer == MAP_FAILED)
return log_debug_errno(errno, "Cannot allocate buffer for hypercall: %m");
- *buffer = (xen_kexec_status_t) {
- .type = KEXEC_TYPE_DEFAULT,
- };
+ if (cmd == KEXEC_CMD_kexec_status)
+ *(xen_kexec_status_t *)buffer = (xen_kexec_status_t) {
+ .type = KEXEC_TYPE_DEFAULT,
+ };
+ else
+ *(xen_kexec_exec_t *)buffer = (xen_kexec_exec_t) {
+ .type = KEXEC_TYPE_DEFAULT,
+ };
privcmd_hypercall_t call = {
.op = __HYPERVISOR_kexec_op,
.arg = {
- KEXEC_CMD_kexec_status,
+ cmd,
PTR_TO_UINT64(buffer),
},
};
r = RET_NERRNO(ioctl(privcmd_fd, IOCTL_PRIVCMD_HYPERCALL, &call));
if (r < 0)
- log_debug_errno(r, "kexec_status failed: %m");
+ log_debug_errno(r, "kexec%s failed: %m", cmd == KEXEC_CMD_kexec_status ? "_status" : "");
munmap(buffer, size);
return r;
+}
+#endif
+
+static int xen_kexec(void) {
+#if HAVE_XENCTRL
+ return xen_kexec_command(KEXEC_CMD_kexec);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+static int xen_kexec_loaded(void) {
+#if HAVE_XENCTRL
+ return xen_kexec_command(KEXEC_CMD_kexec_status);
#else
return -EOPNOTSUPP;
#endif
return s[0] == '1';
}
+int kexec(void) {
+ int r;
+
+ r = xen_kexec();
+ if (r < 0 && r != -EOPNOTSUPP)
+ return log_error_errno(r, "Failed to call xen kexec: %m");
+
+ r = reboot(LINUX_REBOOT_CMD_KEXEC);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to kexec: %m");
+
+ return 0;
+}
+
int create_shutdown_run_nologin_or_warn(void) {
int r;
case LINUX_REBOOT_CMD_KEXEC:
if (!in_container) {
- /* We cheat and exec kexec to avoid doing all its work */
log_info("Rebooting with kexec.");
- r = pidref_safe_fork(
- "(sd-kexec)",
- FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_WAIT,
- /* ret= */ NULL);
- if (r == 0) {
- /* Child */
-
- (void) execl(KEXEC, KEXEC, "-e", NULL);
- log_debug_errno(errno, "Failed to execute '" KEXEC "' binary, proceeding with reboot(RB_KEXEC): %m");
-
- /* execv failed (kexec binary missing?), so try simply reboot(RB_KEXEC) */
- (void) reboot(cmd);
- _exit(EXIT_FAILURE);
- }
+ (void) kexec();
/* If we are still running, then the kexec can't have worked, let's fall through */
}