mirror of
https://github.com/AuxXxilium/eudev.git
synced 2024-12-03 21:27:00 +07:00
shutdownd: rework interface, allow subscribing to scheduled shutdowns
This extends the shutdownd interface to expose schedule shutdown information in /run/systemd/shutdown/schedule. This also cleans up the shutdownd protocol and documents it in a header file sd-shutdown.h. This is supposed to be used by client code that wants to control and monitor scheduled shutdown.
This commit is contained in:
parent
7e59bfcb18
commit
04ebb59567
@ -761,7 +761,6 @@ libsystemd_core_la_SOURCES = \
|
||||
src/bus-errors.h \
|
||||
src/cgroup-show.h \
|
||||
src/build.h \
|
||||
src/shutdownd.h \
|
||||
src/umount.h \
|
||||
src/ask-password-api.h \
|
||||
src/sysfs-show.h \
|
||||
@ -934,9 +933,13 @@ systemd_shutdownd_SOURCES = \
|
||||
src/shutdownd.c
|
||||
|
||||
systemd_shutdownd_LDADD = \
|
||||
libsystemd-shared.la \
|
||||
libsystemd-shared-selinux.la \
|
||||
libsystemd-daemon.la
|
||||
|
||||
pkginclude_HEADERS += \
|
||||
src/systemd/sd-shutdown.h
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
systemd_shutdown_SOURCES = \
|
||||
src/mount-setup.c \
|
||||
src/umount.c \
|
||||
|
230
src/shutdownd.c
230
src/shutdownd.c
@ -28,16 +28,23 @@
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <systemd/sd-daemon.h>
|
||||
#include <systemd/sd-shutdown.h>
|
||||
|
||||
#include "shutdownd.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "util.h"
|
||||
#include "utmp-wtmp.h"
|
||||
#include "mkdir.h"
|
||||
|
||||
static int read_packet(int fd, struct shutdownd_command *_c) {
|
||||
union shutdown_buffer {
|
||||
struct sd_shutdown_command command;
|
||||
char space[offsetof(struct sd_shutdown_command, wall_message) + LINE_MAX];
|
||||
};
|
||||
|
||||
static int read_packet(int fd, union shutdown_buffer *_b) {
|
||||
struct msghdr msghdr;
|
||||
struct iovec iovec;
|
||||
struct ucred *ucred;
|
||||
@ -45,15 +52,15 @@ static int read_packet(int fd, struct shutdownd_command *_c) {
|
||||
struct cmsghdr cmsghdr;
|
||||
uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
|
||||
} control;
|
||||
struct shutdownd_command c;
|
||||
ssize_t n;
|
||||
union shutdown_buffer b; /* We maintain our own copy here, in order not to corrupt the last message */
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(_c);
|
||||
assert(_b);
|
||||
|
||||
zero(iovec);
|
||||
iovec.iov_base = &c;
|
||||
iovec.iov_len = sizeof(c);
|
||||
iovec.iov_base = &b;
|
||||
iovec.iov_len = sizeof(b) - 1;
|
||||
|
||||
zero(control);
|
||||
zero(msghdr);
|
||||
@ -62,8 +69,9 @@ static int read_packet(int fd, struct shutdownd_command *_c) {
|
||||
msghdr.msg_control = &control;
|
||||
msghdr.msg_controllen = sizeof(control);
|
||||
|
||||
if ((n = recvmsg(fd, &msghdr, MSG_DONTWAIT)) <= 0) {
|
||||
if (n >= 0) {
|
||||
n = recvmsg(fd, &msghdr, MSG_DONTWAIT);
|
||||
if (n <= 0) {
|
||||
if (n == 0) {
|
||||
log_error("Short read");
|
||||
return -EIO;
|
||||
}
|
||||
@ -89,18 +97,27 @@ static int read_packet(int fd, struct shutdownd_command *_c) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (n != sizeof(c)) {
|
||||
log_warning("Message has invalid size. Ignoring");
|
||||
if ((size_t) n < offsetof(struct sd_shutdown_command, wall_message)) {
|
||||
log_warning("Message has invalid size. Ignoring.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
char_array_0(c.wall_message);
|
||||
if (b.command.mode != SD_SHUTDOWN_NONE &&
|
||||
b.command.mode != SD_SHUTDOWN_REBOOT &&
|
||||
b.command.mode != SD_SHUTDOWN_POWEROFF &&
|
||||
b.command.mode != SD_SHUTDOWN_HALT &&
|
||||
b.command.mode != SD_SHUTDOWN_KEXEC) {
|
||||
log_warning("Message has invalid mode. Ignoring.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
*_c = c;
|
||||
b.space[n] = 0;
|
||||
|
||||
*_b = b;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void warn_wall(usec_t n, struct shutdownd_command *c) {
|
||||
static void warn_wall(usec_t n, struct sd_shutdown_command *c) {
|
||||
char date[FORMAT_TIMESTAMP_MAX];
|
||||
const char *prefix;
|
||||
char *l = NULL;
|
||||
@ -108,20 +125,22 @@ static void warn_wall(usec_t n, struct shutdownd_command *c) {
|
||||
assert(c);
|
||||
assert(c->warn_wall);
|
||||
|
||||
if (n >= c->elapse)
|
||||
if (n >= c->usec)
|
||||
return;
|
||||
|
||||
if (c->mode == 'H')
|
||||
if (c->mode == SD_SHUTDOWN_HALT)
|
||||
prefix = "The system is going down for system halt at ";
|
||||
else if (c->mode == 'P')
|
||||
else if (c->mode == SD_SHUTDOWN_POWEROFF)
|
||||
prefix = "The system is going down for power-off at ";
|
||||
else if (c->mode == 'r')
|
||||
else if (c->mode == SD_SHUTDOWN_REBOOT)
|
||||
prefix = "The system is going down for reboot at ";
|
||||
else if (c->mode == SD_SHUTDOWN_KEXEC)
|
||||
prefix = "The system is going down for kexec reboot at ";
|
||||
else
|
||||
assert_not_reached("Unknown mode!");
|
||||
|
||||
if (asprintf(&l, "%s%s%s%s!", c->wall_message, c->wall_message[0] ? "\n" : "",
|
||||
prefix, format_timestamp(date, sizeof(date), c->elapse)) < 0)
|
||||
prefix, format_timestamp(date, sizeof(date), c->usec)) < 0)
|
||||
log_error("Failed to allocate wall message");
|
||||
else {
|
||||
utmp_wall(l, NULL);
|
||||
@ -164,6 +183,85 @@ static usec_t when_nologin(usec_t elapse) {
|
||||
return elapse > 5*USEC_PER_MINUTE ? elapse - 5*USEC_PER_MINUTE : 1;
|
||||
}
|
||||
|
||||
static const char *mode_to_string(enum sd_shutdown_mode m) {
|
||||
switch (m) {
|
||||
case SD_SHUTDOWN_REBOOT:
|
||||
return "reboot";
|
||||
case SD_SHUTDOWN_POWEROFF:
|
||||
return "poweroff";
|
||||
case SD_SHUTDOWN_HALT:
|
||||
return "halt";
|
||||
case SD_SHUTDOWN_KEXEC:
|
||||
return "kexec";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int update_schedule_file(struct sd_shutdown_command *c) {
|
||||
int r;
|
||||
FILE *f;
|
||||
char *temp_path, *t;
|
||||
|
||||
assert(c);
|
||||
|
||||
r = safe_mkdir("/run/systemd/shutdown", 0755, 0, 0);
|
||||
if (r < 0) {
|
||||
log_error("Failed to create shutdown subdirectory: %s", strerror(-r));
|
||||
return r;
|
||||
}
|
||||
|
||||
t = cescape(c->wall_message);
|
||||
if (!t) {
|
||||
log_error("Out of memory");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path);
|
||||
if (r < 0) {
|
||||
log_error("Failed to save information about scheduled shutdowns: %s", strerror(-r));
|
||||
free(t);
|
||||
return r;
|
||||
}
|
||||
|
||||
fchmod(fileno(f), 0644);
|
||||
|
||||
fprintf(f,
|
||||
"USEC=%llu\n"
|
||||
"WARN_WALL=%i\n"
|
||||
"MODE=%s\n",
|
||||
(unsigned long long) c->usec,
|
||||
c->warn_wall,
|
||||
mode_to_string(c->mode));
|
||||
|
||||
if (c->dry_run)
|
||||
fputs("DRY_RUN=1\n", f);
|
||||
|
||||
if (!isempty(t))
|
||||
fprintf(f, "WALL_MESSAGE=%s\n", t);
|
||||
|
||||
free(t);
|
||||
|
||||
fflush(f);
|
||||
|
||||
if (ferror(f) || rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) {
|
||||
log_error("Failed to write information about scheduled shutdowns: %m");
|
||||
r = -errno;
|
||||
|
||||
unlink(temp_path);
|
||||
unlink("/run/systemd/shutdown/scheduled");
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
free(temp_path);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static bool scheduled(struct sd_shutdown_command *c) {
|
||||
return c->usec > 0 && c->mode != SD_SHUTDOWN_NONE;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
enum {
|
||||
FD_SOCKET,
|
||||
@ -174,9 +272,9 @@ int main(int argc, char *argv[]) {
|
||||
};
|
||||
|
||||
int r = EXIT_FAILURE, n_fds;
|
||||
struct shutdownd_command c;
|
||||
union shutdown_buffer b;
|
||||
struct pollfd pollfd[_FD_MAX];
|
||||
bool exec_shutdown = false, unlink_nologin = false, failed = false;
|
||||
bool exec_shutdown = false, unlink_nologin = false;
|
||||
unsigned i;
|
||||
|
||||
if (getppid() != 1) {
|
||||
@ -195,7 +293,8 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
umask(0022);
|
||||
|
||||
if ((n_fds = sd_listen_fds(true)) < 0) {
|
||||
n_fds = sd_listen_fds(true);
|
||||
if (n_fds < 0) {
|
||||
log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
@ -205,39 +304,33 @@ int main(int argc, char *argv[]) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
zero(c);
|
||||
zero(b);
|
||||
zero(pollfd);
|
||||
|
||||
pollfd[FD_SOCKET].fd = SD_LISTEN_FDS_START;
|
||||
pollfd[FD_SOCKET].events = POLLIN;
|
||||
|
||||
for (i = 0; i < _FD_MAX; i++) {
|
||||
|
||||
if (i == FD_SOCKET)
|
||||
continue;
|
||||
|
||||
for (i = FD_WALL_TIMER; i < _FD_MAX; i++) {
|
||||
pollfd[i].events = POLLIN;
|
||||
|
||||
if ((pollfd[i].fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC)) < 0) {
|
||||
pollfd[i].fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
|
||||
if (pollfd[i].fd < 0) {
|
||||
log_error("timerfd_create(): %m");
|
||||
failed = true;
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
if (failed)
|
||||
goto finish;
|
||||
|
||||
log_debug("systemd-shutdownd running as pid %lu", (unsigned long) getpid());
|
||||
|
||||
sd_notify(false,
|
||||
"READY=1\n"
|
||||
"STATUS=Processing requests...");
|
||||
|
||||
do {
|
||||
for (;;) {
|
||||
int k;
|
||||
usec_t n;
|
||||
|
||||
if (poll(pollfd, _FD_MAX, -1) < 0) {
|
||||
k = poll(pollfd, _FD_MAX, scheduled(&b.command) ? -1 : 0);
|
||||
if (k < 0) {
|
||||
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
continue;
|
||||
@ -246,34 +339,44 @@ int main(int argc, char *argv[]) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Exit on idle */
|
||||
if (k == 0)
|
||||
break;
|
||||
|
||||
n = now(CLOCK_REALTIME);
|
||||
|
||||
if (pollfd[FD_SOCKET].revents) {
|
||||
|
||||
if ((k = read_packet(pollfd[FD_SOCKET].fd, &c)) < 0)
|
||||
k = read_packet(pollfd[FD_SOCKET].fd, &b);
|
||||
if (k < 0)
|
||||
goto finish;
|
||||
else if (k > 0 && c.elapse > 0) {
|
||||
else if (k > 0) {
|
||||
struct itimerspec its;
|
||||
char date[FORMAT_TIMESTAMP_MAX];
|
||||
|
||||
if (c.warn_wall) {
|
||||
if (!scheduled(&b.command)) {
|
||||
log_info("Shutdown canceled.");
|
||||
break;
|
||||
}
|
||||
|
||||
zero(its);
|
||||
if (b.command.warn_wall) {
|
||||
/* Send wall messages every so often */
|
||||
zero(its);
|
||||
timespec_store(&its.it_value, when_wall(n, c.elapse));
|
||||
if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
|
||||
log_error("timerfd_settime(): %m");
|
||||
goto finish;
|
||||
}
|
||||
timespec_store(&its.it_value, when_wall(n, b.command.usec));
|
||||
|
||||
/* Warn immediately if less than 15 minutes are left */
|
||||
if (n < c.elapse &&
|
||||
n + 15*USEC_PER_MINUTE >= c.elapse)
|
||||
warn_wall(n, &c);
|
||||
if (n < b.command.usec &&
|
||||
n + 15*USEC_PER_MINUTE >= b.command.usec)
|
||||
warn_wall(n, &b.command);
|
||||
}
|
||||
if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
|
||||
log_error("timerfd_settime(): %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Disallow logins 5 minutes prior to shutdown */
|
||||
zero(its);
|
||||
timespec_store(&its.it_value, when_nologin(c.elapse));
|
||||
timespec_store(&its.it_value, when_nologin(b.command.usec));
|
||||
if (timerfd_settime(pollfd[FD_NOLOGIN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
|
||||
log_error("timerfd_settime(): %m");
|
||||
goto finish;
|
||||
@ -281,27 +384,32 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
/* Shutdown after the specified time is reached */
|
||||
zero(its);
|
||||
timespec_store(&its.it_value, c.elapse);
|
||||
timespec_store(&its.it_value, b.command.usec);
|
||||
if (timerfd_settime(pollfd[FD_SHUTDOWN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
|
||||
log_error("timerfd_settime(): %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
update_schedule_file(&b.command);
|
||||
|
||||
sd_notifyf(false,
|
||||
"STATUS=Shutting down at %s...",
|
||||
format_timestamp(date, sizeof(date), c.elapse));
|
||||
"STATUS=Shutting down at %s (%s)...",
|
||||
format_timestamp(date, sizeof(date), b.command.usec),
|
||||
mode_to_string(b.command.mode));
|
||||
|
||||
log_info("Shutting down at %s (%s)...", date, mode_to_string(b.command.mode));
|
||||
}
|
||||
}
|
||||
|
||||
if (pollfd[FD_WALL_TIMER].revents) {
|
||||
struct itimerspec its;
|
||||
|
||||
warn_wall(n, &c);
|
||||
warn_wall(n, &b.command);
|
||||
flush_fd(pollfd[FD_WALL_TIMER].fd);
|
||||
|
||||
/* Restart timer */
|
||||
zero(its);
|
||||
timespec_store(&its.it_value, when_wall(n, c.elapse));
|
||||
timespec_store(&its.it_value, when_wall(n, b.command.usec));
|
||||
if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
|
||||
log_error("timerfd_settime(): %m");
|
||||
goto finish;
|
||||
@ -313,7 +421,8 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
log_info("Creating /run/nologin, blocking further logins...");
|
||||
|
||||
if ((e = write_one_line_file_atomic("/run/nologin", "System is going down.")) < 0)
|
||||
e = write_one_line_file_atomic("/run/nologin", "System is going down.");
|
||||
if (e < 0)
|
||||
log_error("Failed to create /run/nologin: %s", strerror(-e));
|
||||
else
|
||||
unlink_nologin = true;
|
||||
@ -325,8 +434,7 @@ int main(int argc, char *argv[]) {
|
||||
exec_shutdown = true;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
} while (c.elapse > 0);
|
||||
}
|
||||
|
||||
r = EXIT_SUCCESS;
|
||||
|
||||
@ -341,19 +449,21 @@ finish:
|
||||
if (unlink_nologin)
|
||||
unlink("/run/nologin");
|
||||
|
||||
if (exec_shutdown && !c.dry_run) {
|
||||
unlink("/run/systemd/shutdown/scheduled");
|
||||
|
||||
if (exec_shutdown && !b.command.dry_run) {
|
||||
char sw[3];
|
||||
|
||||
sw[0] = '-';
|
||||
sw[1] = c.mode;
|
||||
sw[1] = b.command.mode;
|
||||
sw[2] = 0;
|
||||
|
||||
execl(SYSTEMCTL_BINARY_PATH,
|
||||
"shutdown",
|
||||
sw,
|
||||
"now",
|
||||
(c.warn_wall && c.wall_message[0]) ? c.wall_message :
|
||||
(c.warn_wall ? NULL : "--no-wall"),
|
||||
(b.command.warn_wall && b.command.wall_message[0]) ? b.command.wall_message :
|
||||
(b.command.warn_wall ? NULL : "--no-wall"),
|
||||
NULL);
|
||||
|
||||
log_error("Failed to execute /sbin/shutdown: %m");
|
||||
|
@ -1,46 +0,0 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
#ifndef fooshutdowndhfoo
|
||||
#define fooshutdowndhfoo
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2010 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include "util.h"
|
||||
#include "macro.h"
|
||||
|
||||
/* This is a private message, we don't care much about ABI
|
||||
* stability. */
|
||||
|
||||
_packed_ struct shutdownd_command {
|
||||
usec_t elapse;
|
||||
char mode; /* H, P, r, i.e. the switches usually passed to
|
||||
* shutdown to select whether to halt, power-off or
|
||||
* reboot the machine */
|
||||
bool dry_run;
|
||||
bool warn_wall;
|
||||
|
||||
/* Yepp, sometimes we are lazy and use fixed-size strings like
|
||||
* this one. Shame on us. But then again, we'd have to
|
||||
* pre-allocate the receive buffer anyway, so there's nothing
|
||||
* too bad here. */
|
||||
char wall_message[4096];
|
||||
};
|
||||
|
||||
#endif
|
@ -36,6 +36,7 @@
|
||||
#include <dbus/dbus.h>
|
||||
|
||||
#include <systemd/sd-daemon.h>
|
||||
#include <systemd/sd-shutdown.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
@ -51,7 +52,6 @@
|
||||
#include "list.h"
|
||||
#include "path-lookup.h"
|
||||
#include "conf-parser.h"
|
||||
#include "shutdownd.h"
|
||||
#include "exit-status.h"
|
||||
#include "bus-errors.h"
|
||||
#include "build.h"
|
||||
@ -4628,6 +4628,7 @@ static int shutdown_parse_argv(int argc, char *argv[]) {
|
||||
{ "halt", no_argument, NULL, 'H' },
|
||||
{ "poweroff", no_argument, NULL, 'P' },
|
||||
{ "reboot", no_argument, NULL, 'r' },
|
||||
{ "kexec", no_argument, NULL, 'K' }, /* not documented extension */
|
||||
{ "no-wall", no_argument, NULL, ARG_NO_WALL },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
@ -4659,6 +4660,10 @@ static int shutdown_parse_argv(int argc, char *argv[]) {
|
||||
arg_action = ACTION_REBOOT;
|
||||
break;
|
||||
|
||||
case 'K':
|
||||
arg_action = ACTION_KEXEC;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
if (arg_action != ACTION_HALT)
|
||||
arg_action = ACTION_POWEROFF;
|
||||
@ -5191,39 +5196,42 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
|
||||
}
|
||||
|
||||
static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const char *message) {
|
||||
int fd = -1;
|
||||
int fd;
|
||||
struct msghdr msghdr;
|
||||
struct iovec iovec;
|
||||
struct iovec iovec[2];
|
||||
union sockaddr_union sockaddr;
|
||||
struct shutdownd_command c;
|
||||
struct sd_shutdown_command c;
|
||||
|
||||
fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
zero(c);
|
||||
c.elapse = t;
|
||||
c.usec = t;
|
||||
c.mode = mode;
|
||||
c.dry_run = dry_run;
|
||||
c.warn_wall = warn;
|
||||
|
||||
if (message)
|
||||
strncpy(c.wall_message, message, sizeof(c.wall_message));
|
||||
|
||||
if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0)
|
||||
return -errno;
|
||||
|
||||
zero(sockaddr);
|
||||
sockaddr.sa.sa_family = AF_UNIX;
|
||||
sockaddr.un.sun_path[0] = 0;
|
||||
strncpy(sockaddr.un.sun_path, "/run/systemd/shutdownd", sizeof(sockaddr.un.sun_path));
|
||||
|
||||
zero(iovec);
|
||||
iovec.iov_base = (char*) &c;
|
||||
iovec.iov_len = sizeof(c);
|
||||
|
||||
zero(msghdr);
|
||||
msghdr.msg_name = &sockaddr;
|
||||
msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + sizeof("/run/systemd/shutdownd") - 1;
|
||||
|
||||
msghdr.msg_iov = &iovec;
|
||||
msghdr.msg_iovlen = 1;
|
||||
zero(iovec);
|
||||
iovec[0].iov_base = (char*) &c;
|
||||
iovec[0].iov_len = offsetof(struct sd_shutdown_command, wall_message);
|
||||
|
||||
if (isempty(message))
|
||||
msghdr.msg_iovlen = 1;
|
||||
else {
|
||||
iovec[1].iov_base = (char*) message;
|
||||
iovec[1].iov_len = strlen(message);
|
||||
msghdr.msg_iovlen = 2;
|
||||
}
|
||||
msghdr.msg_iov = iovec;
|
||||
|
||||
if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
|
||||
close_nointr_nofail(fd);
|
||||
@ -5338,6 +5346,7 @@ static int halt_main(DBusConnection *bus) {
|
||||
r = send_shutdownd(arg_when,
|
||||
arg_action == ACTION_HALT ? 'H' :
|
||||
arg_action == ACTION_POWEROFF ? 'P' :
|
||||
arg_action == ACTION_KEXEC ? 'K' :
|
||||
'r',
|
||||
arg_dry,
|
||||
!arg_no_wall,
|
||||
@ -5405,7 +5414,8 @@ int main(int argc, char*argv[]) {
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
|
||||
if ((r = parse_argv(argc, argv)) < 0)
|
||||
r = parse_argv(argc, argv);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
else if (r == 0) {
|
||||
retval = EXIT_SUCCESS;
|
||||
|
108
src/systemd/sd-shutdown.h
Normal file
108
src/systemd/sd-shutdown.h
Normal file
@ -0,0 +1,108 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
#ifndef foosdshutdownhfoo
|
||||
#define foosdshutdownhfoo
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2010 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
/* Interface for scheduling and cancelling timed shutdowns. */
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
typedef enum sd_shutdown_mode {
|
||||
SD_SHUTDOWN_NONE = 0,
|
||||
SD_SHUTDOWN_REBOOT = 'r',
|
||||
SD_SHUTDOWN_POWEROFF = 'P',
|
||||
SD_SHUTDOWN_HALT = 'H',
|
||||
SD_SHUTDOWN_KEXEC = 'K'
|
||||
} sd_shutdown_mode_t;
|
||||
|
||||
/* Calculate the size of the message as "offsetof(struct
|
||||
* sd_shutdown_command, wall_message) +
|
||||
* strlen(command.wall_message)" */
|
||||
__attribute__((packed)) struct sd_shutdown_command {
|
||||
/* Microseconds after the epoch 1970 UTC */
|
||||
uint64_t usec;
|
||||
|
||||
/* H, P, r, i.e. the switches usually passed to
|
||||
* /usr/bin/shutdown to select whether to halt, power-off or
|
||||
* reboot the machine */
|
||||
sd_shutdown_mode_t mode:8;
|
||||
|
||||
/* If non-zero, don't actually shut down, just pretend */
|
||||
unsigned dry_run:1;
|
||||
|
||||
/* If non-zero, send our wall message */
|
||||
unsigned warn_wall:1;
|
||||
|
||||
/* The wall message to send around. Leave empty for the
|
||||
* default wall message */
|
||||
char wall_message[];
|
||||
};
|
||||
|
||||
/* The scheme is very simple:
|
||||
*
|
||||
* To schedule a shutdown, simply fill in and send a single
|
||||
* AF_UNIX/SOCK_DGRAM datagram with the structure above suffixed with
|
||||
* the wall message to the socket /run/systemd/shutdownd (leave an
|
||||
* empty wall message for the default shutdown message). To calculate
|
||||
* the size of the message use "offsetof(struct sd_shutdown_command,
|
||||
* wall_message) + strlen(command.wall_message)".
|
||||
*
|
||||
* To cancel a shutdown, do the same, but send an fully zeroed out
|
||||
* structure.
|
||||
*
|
||||
* To be notified about scheduled shutdowns, create an inotify watch
|
||||
* on /run/shutdown/. Whenever a file called "scheduled" appears a
|
||||
* shutdown is scheduled. If it is removed it is canceled. It is
|
||||
* replaced the scheduled shutdown has been changed. The file contains
|
||||
* a simple environment-like block, that contains information about
|
||||
* the scheduled shutdown:
|
||||
*
|
||||
* USEC=
|
||||
* encodes the time for the shutdown in usecs since the epoch UTC,
|
||||
* formatted as numeric string.
|
||||
*
|
||||
* WARN_WALL=
|
||||
* is 1 if a wall message shall be sent
|
||||
*
|
||||
* DRY_RUN=
|
||||
* is 1 if a dry run shutdown is scheduled
|
||||
*
|
||||
* MODE=
|
||||
* is the shutdown mode, one of "poweroff", "reboot", "halt", "kexec"
|
||||
*
|
||||
* WALL_MESSAGE=
|
||||
* is the wall message to use, with all special characters escape in C style.
|
||||
*
|
||||
* Note that some fields might be missing if they do not apply.
|
||||
*
|
||||
* Note that the file is first written to a temporary file and then
|
||||
* renamed, in order to provide atomic properties for readers: if the
|
||||
* file exists under the name "scheduled" it is guaranteed to be fully
|
||||
* written. A reader should ignore all files in that directory by any
|
||||
* other name.
|
||||
*
|
||||
* Scheduled shutdowns are only accepted from privileged processes,
|
||||
* but the directory may be watched and the file in it read by
|
||||
* anybody.
|
||||
*/
|
||||
|
||||
#endif
|
@ -23,3 +23,4 @@ d /run/systemd/ask-password 0755 root root -
|
||||
d /run/systemd/seats 0755 root root -
|
||||
d /run/systemd/sessions 0755 root root -
|
||||
d /run/systemd/users 0755 root root -
|
||||
d /run/systemd/shutdown 0755 root root -
|
||||
|
Loading…
Reference in New Issue
Block a user