machinectl: add new command to spawn a getty inside a container

This commit is contained in:
Lennart Poettering 2013-10-31 01:25:44 +01:00
parent f85fc84541
commit 04d3927924
11 changed files with 490 additions and 153 deletions

View File

@ -417,6 +417,7 @@ nodist_systemunit_DATA = \
units/serial-getty@.service \
units/console-shell.service \
units/console-getty.service \
units/container-getty@.service \
units/systemd-initctl.service \
units/systemd-shutdownd.service \
units/systemd-remount-fs.service \
@ -459,6 +460,7 @@ EXTRA_DIST += \
units/serial-getty@.service.m4 \
units/console-shell.service.m4.in \
units/console-getty.service.m4.in \
units/container-getty@.service.m4.in \
units/rescue.service.m4.in \
units/systemd-initctl.service.in \
units/systemd-shutdownd.service.in \
@ -494,6 +496,7 @@ EXTRA_DIST += \
CLEANFILES += \
units/console-shell.service.m4 \
units/console-getty.service.m4 \
units/container-getty@.service.m4 \
units/rescue.service.m4
if HAVE_SYSV_COMPAT

View File

@ -263,6 +263,19 @@
<option>--signal=</option> to select
the signal to send.</para></listitem>
</varlistentry>
<varlistentry>
<term><command>login [ID]</command></term>
<listitem><para>Open a terminal login
session to a container. This will
create a TTY connection to a specific
container and asks for execution of a
getty on it. Note that this is only
supported for containers running
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
as init system.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>

View File

@ -123,10 +123,10 @@
see each other. The PID namespace separation of the
two containers is complete and the containers will
share very few runtime objects except for the
underlying file system. It is however possible to
enter an existing container, see
<link linkend='example-nsenter'>Example 4</link> below.
</para>
underlying file system. Use
<citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
<command>login</command> command to request an
additional login prompt in a running container.</para>
<para><command>systemd-nspawn</command> implements the
<ulink
@ -409,24 +409,6 @@
boots an OS in a namespace container in it.</para>
</refsect1>
<refsect1 id='example-nsenter'>
<title>Example 4</title>
<para>To enter the container, PID of one of the
processes sharing the new namespaces must be used.
<command>systemd-nspawn</command> prints the PID
(as viewed from the outside) of the launched process,
and it can be used to enter the container.</para>
<programlisting># nsenter -m -u -i -n -p -t $PID</programlisting>
<para><citerefentry><refentrytitle>nsenter</refentrytitle><manvolnum>1</manvolnum></citerefentry>
is part of
<ulink url="https://github.com/karelzak/util-linux">util-linux</ulink>.
Kernel support for entering namespaces was added in
Linux 3.8.</para>
</refsect1>
<refsect1>
<title>Exit status</title>
@ -439,11 +421,11 @@
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>chroot</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>unshare</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>yum</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>debootstrap</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>pacman</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry>
<citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
</para>
</refsect1>

View File

@ -72,7 +72,7 @@ int bus_container_connect(sd_bus *b) {
if (r < 0)
return -ENOMEM;
rootfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC);
rootfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
if (rootfd < 0)
return -errno;
@ -101,7 +101,6 @@ int bus_container_connect(sd_bus *b) {
if (chroot(".") < 0)
_exit(255);
r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size);
if (r < 0) {
if (errno == EINPROGRESS)

View File

@ -25,6 +25,8 @@
#include <getopt.h>
#include <pwd.h>
#include <locale.h>
#include <socket.h>
#include <fcntl.h>
#include "sd-bus.h"
#include "log.h"
@ -38,6 +40,7 @@
#include "unit-name.h"
#include "cgroup-show.h"
#include "cgroup-util.h"
#include "ptyfwd.h"
static char **arg_property = NULL;
static bool arg_all = false;
@ -515,6 +518,239 @@ static int terminate_machine(sd_bus *bus, char **args, unsigned n) {
return 0;
}
static int openpt_in_namespace(pid_t pid, int flags) {
_cleanup_close_ int nsfd = -1, rootfd = -1;
_cleanup_free_ char *ns = NULL, *root = NULL;
_cleanup_close_pipe_ int sock[2] = { -1, -1 };
struct msghdr mh;
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(int))];
} control;
struct cmsghdr *cmsg;
int master, r;
pid_t child;
siginfo_t si;
r = asprintf(&ns, "/proc/%lu/ns/mnt", (unsigned long) pid);
if (r < 0)
return -ENOMEM;
nsfd = open(ns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
if (nsfd < 0)
return -errno;
r = asprintf(&root, "/proc/%lu/root", (unsigned long) pid);
if (r < 0)
return -ENOMEM;
rootfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
if (rootfd < 0)
return -errno;
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sock) < 0)
return -errno;
zero(control);
zero(mh);
mh.msg_control = &control;
mh.msg_controllen = sizeof(control);
child = fork();
if (child < 0)
return -errno;
if (child == 0) {
close_nointr_nofail(sock[0]);
sock[0] = -1;
r = setns(nsfd, CLONE_NEWNS);
if (r < 0)
_exit(EXIT_FAILURE);
if (fchdir(rootfd) < 0)
_exit(EXIT_FAILURE);
if (chroot(".") < 0)
_exit(EXIT_FAILURE);
master = posix_openpt(flags);
if (master < 0)
_exit(EXIT_FAILURE);
cmsg = CMSG_FIRSTHDR(&mh);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
mh.msg_controllen = cmsg->cmsg_len;
r = sendmsg(sock[1], &mh, MSG_NOSIGNAL);
close_nointr_nofail(master);
if (r < 0)
_exit(EXIT_FAILURE);
_exit(EXIT_SUCCESS);
}
close_nointr_nofail(sock[1]);
sock[1] = -1;
if (recvmsg(sock[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
return -errno;
for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
int *fds;
unsigned n_fds;
fds = (int*) CMSG_DATA(cmsg);
n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
if (n_fds != 1) {
close_many(fds, n_fds);
return -EIO;
}
master = fds[0];
}
r = wait_for_terminate(child, &si);
if (r < 0 || si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS || master < 0) {
if (master >= 0)
close_nointr_nofail(master);
return r < 0 ? r : -EIO;
}
return master;
}
static int login_machine(sd_bus *bus, char **args, unsigned n) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL, *reply3 = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_bus_unref_ sd_bus *container_bus = NULL;
_cleanup_close_ int master = -1;
_cleanup_free_ char *getty = NULL;
const char *path, *pty, *p;
uint32_t leader;
sigset_t mask;
int r;
assert(bus);
assert(args);
if (arg_transport != BUS_TRANSPORT_LOCAL) {
log_error("Login only support on local machines.");
return -ENOTSUP;
}
r = sd_bus_call_method(
bus,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"GetMachine",
&error,
&reply,
"s", args[1]);
if (r < 0) {
log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
return r;
}
r = sd_bus_message_read(reply, "o", &path);
if (r < 0) {
log_error("Failed to parse reply: %s", strerror(-r));
return r;
}
r = sd_bus_get_property(
bus,
"org.freedesktop.machine1",
path,
"org.freedesktop.machine1.Machine",
"Leader",
&error,
&reply2,
"u");
if (r < 0) {
log_error("Failed to retrieve PID of leader: %s", strerror(-r));
return r;
}
r = sd_bus_message_read(reply2, "u", &leader);
if (r < 0) {
log_error("Failed to parse reply: %s", strerror(-r));
return r;
}
master = openpt_in_namespace(leader, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
if (master < 0) {
log_error("Failed to acquire pseudo tty: %s", strerror(-master));
return master;
}
pty = ptsname(master);
if (!pty) {
log_error("Failed to get pty name: %m");
return -errno;
}
p = startswith(pty, "/dev/pts/");
if (!p) {
log_error("Invalid pty name %s.", pty);
return -EIO;
}
r = sd_bus_open_system_container(args[1], &container_bus);
if (r < 0) {
log_error("Failed to get container bus: %s", strerror(-r));
return r;
}
getty = strjoin("container-getty@", p, ".service", NULL);
if (!getty)
return log_oom();
if (unlockpt(master) < 0) {
log_error("Failed to unlock tty: %m");
return -errno;
}
r = sd_bus_call_method(container_bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartUnit",
&error, &reply3,
"ss", getty, "replace");
if (r < 0) {
log_error("Failed to start getty service: %s", bus_error_message(&error, r));
return r;
}
assert_se(sigemptyset(&mask) == 0);
sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
log_info("Connected to container %s. Press ^] three times within 1s to exit session.", args[1]);
r = process_pty(master, &mask, 0, 0);
if (r < 0) {
log_error("Failed to process pseudo tty: %s", strerror(-r));
return r;
}
fputc('\n', stdout);
log_info("Connection to container %s terminated.", args[1]);
return 0;
}
static int help(void) {
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
@ -535,7 +771,8 @@ static int help(void) {
" status [NAME...] Show VM/container status\n"
" show [NAME...] Show properties of one or more VMs/containers\n"
" terminate [NAME...] Terminate one or more VMs/containers\n"
" kill [NAME...] Send signal to processes of a VM/container\n",
" kill [NAME...] Send signal to processes of a VM/container\n"
" login [NAME] Get a login prompt on a container\n",
program_invocation_short_name);
return 0;
@ -644,7 +881,7 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
static int machinectl_main(sd_bus *bus, int argc, char *argv[], const int r) {
static int machinectl_main(sd_bus *bus, int argc, char *argv[]) {
static const struct {
const char* verb;
@ -661,6 +898,7 @@ static int machinectl_main(sd_bus *bus, int argc, char *argv[], const int r) {
{ "show", MORE, 1, show },
{ "terminate", MORE, 2, terminate_machine },
{ "kill", MORE, 2, kill_machine },
{ "login", MORE, 2, login_machine },
};
int left;
@ -720,11 +958,6 @@ static int machinectl_main(sd_bus *bus, int argc, char *argv[], const int r) {
assert_not_reached("Unknown comparison operator.");
}
if (r < 0) {
log_error("Failed to get D-Bus connection: %s", strerror(-r));
return -EIO;
}
return verbs[i].dispatch(bus, argv + optind, left);
}
@ -751,7 +984,7 @@ int main(int argc, char*argv[]) {
goto finish;
}
r = machinectl_main(bus, argc, argv, r);
r = machinectl_main(bus, argc, argv);
ret = r < 0 ? EXIT_FAILURE : r;
finish:

View File

@ -976,11 +976,8 @@ int main(int argc, char *argv[]) {
_cleanup_close_ int master = -1;
int n_fd_passed;
const char *console = NULL;
struct termios saved_attr, raw_attr;
sigset_t mask;
bool saved_attr_valid = false;
struct winsize ws;
int kmsg_socket_pair[2] = { -1, -1 };
_cleanup_close_pipe_ int kmsg_socket_pair[2] = { -1, -1 };
_cleanup_fdset_free_ FDSet *fds = NULL;
log_parse_environment();
@ -1075,24 +1072,13 @@ int main(int argc, char *argv[]) {
goto finish;
}
log_info("Spawning namespace container on %s (console is %s).", arg_directory, console);
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0)
ioctl(master, TIOCSWINSZ, &ws);
log_info("Spawning container %s on %s. Press ^] three times within 1s to abort execution.", arg_machine, arg_directory);
if (unlockpt(master) < 0) {
log_error("Failed to unlock tty: %m");
goto finish;
}
if (tcgetattr(STDIN_FILENO, &saved_attr) >= 0) {
saved_attr_valid = true;
raw_attr = saved_attr;
cfmakeraw(&raw_attr);
raw_attr.c_lflag &= ~ECHO;
}
if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0, kmsg_socket_pair) < 0) {
log_error("Failed to create kmsg socket pair.");
goto finish;
@ -1106,18 +1092,6 @@ int main(int argc, char *argv[]) {
for (;;) {
siginfo_t status;
int pipefd[2], pipefd2[2];
if (pipe2(pipefd, O_NONBLOCK|O_CLOEXEC) < 0) {
log_error("pipe2(): %m");
goto finish;
}
if (pipe2(pipefd2, O_NONBLOCK|O_CLOEXEC) < 0) {
log_error("pipe2(): %m");
close_pipe(pipefd);
goto finish;
}
pid = syscall(__NR_clone, SIGCHLD|CLONE_NEWIPC|CLONE_NEWNS|CLONE_NEWPID|CLONE_NEWUTS|(arg_private_network ? CLONE_NEWNET : 0), NULL);
if (pid < 0) {
@ -1152,21 +1126,9 @@ int main(int argc, char *argv[]) {
if (envp[n_env])
n_env ++;
/* Wait for the parent process to log our PID */
close_nointr_nofail(pipefd[1]);
fd_wait_for_event(pipefd[0], POLLHUP, -1);
close_nointr_nofail(pipefd[0]);
close_nointr_nofail(master);
master = -1;
if (saved_attr_valid) {
if (tcsetattr(STDIN_FILENO, TCSANOW, &raw_attr) < 0) {
log_error("Failed to set terminal attributes: %m");
goto child_fail;
}
}
close_nointr(STDIN_FILENO);
close_nointr(STDOUT_FILENO);
close_nointr(STDERR_FILENO);
@ -1206,8 +1168,6 @@ int main(int argc, char *argv[]) {
goto child_fail;
}
close_pipe(pipefd2);
r = register_machine();
if (r < 0)
goto finish;
@ -1416,25 +1376,23 @@ int main(int argc, char *argv[]) {
_exit(EXIT_FAILURE);
}
log_info("Init process in the container running as PID %lu.", (unsigned long) pid);
close_nointr_nofail(pipefd[0]);
close_nointr_nofail(pipefd[1]);
/* Wait for the child process to establish cgroup hierarchy */
close_nointr_nofail(pipefd2[1]);
fd_wait_for_event(pipefd2[0], POLLHUP, -1);
close_nointr_nofail(pipefd2[0]);
fdset_free(fds);
fds = NULL;
if (process_pty(master, &mask, arg_boot ? pid : 0, SIGRTMIN+3) < 0)
goto finish;
k = process_pty(master, &mask, arg_boot ? pid : 0, SIGRTMIN+3);
if (k < 0) {
r = EXIT_FAILURE;
break;
}
if (saved_attr_valid)
tcsetattr(STDIN_FILENO, TCSANOW, &saved_attr);
putc('\n', stdout);
/* Kill if it is not dead yet anyway */
kill(pid, SIGKILL);
k = wait_for_terminate(pid, &status);
pid = 0;
if (k < 0) {
r = EXIT_FAILURE;
break;
@ -1443,40 +1401,35 @@ int main(int argc, char *argv[]) {
if (status.si_code == CLD_EXITED) {
r = status.si_status;
if (status.si_status != 0) {
log_error("Container failed with error code %i.", status.si_status);
log_error("Container %s failed with error code %i.", arg_machine, status.si_status);
break;
}
log_debug("Container exited successfully.");
log_debug("Container %s exited successfully.", arg_machine);
break;
} else if (status.si_code == CLD_KILLED &&
status.si_status == SIGINT) {
log_info("Container has been shut down.");
log_info("Container %s has been shut down.", arg_machine);
r = 0;
break;
} else if (status.si_code == CLD_KILLED &&
status.si_status == SIGHUP) {
log_info("Container is being rebooted.");
log_info("Container %s is being rebooted.", arg_machine);
continue;
} else if (status.si_code == CLD_KILLED ||
status.si_code == CLD_DUMPED) {
log_error("Container terminated by signal %s.", signal_to_string(status.si_status));
log_error("Container %s terminated by signal %s.", arg_machine, signal_to_string(status.si_status));
r = EXIT_FAILURE;
break;
} else {
log_error("Container failed due to unknown reason.");
log_error("Container %s failed due to unknown reason.", arg_machine);
r = EXIT_FAILURE;
break;
}
}
finish:
if (saved_attr_valid)
tcsetattr(STDIN_FILENO, TCSANOW, &saved_attr);
close_pipe(kmsg_socket_pair);
if (pid > 0)
kill(pid, SIGKILL);

View File

@ -28,12 +28,49 @@
#include "util.h"
#include "ptyfwd.h"
int process_pty(int master, sigset_t *mask, pid_t kill_pid, int signo) {
#define ESCAPE_USEC USEC_PER_SEC
static bool look_for_escape(usec_t *timestamp, unsigned *counter, const char *buffer, size_t n) {
const char *p;
assert(timestamp);
assert(counter);
assert(buffer);
assert(n > 0);
for (p = buffer; p < buffer + n; p++) {
/* Check for ^] */
if (*p == 0x1D) {
usec_t nw = now(CLOCK_MONOTONIC);
if (*counter == 0 || nw > *timestamp + USEC_PER_SEC) {
*timestamp = nw;
*counter = 1;
} else {
(*counter)++;
if (*counter >= 3)
return true;
}
} else {
*timestamp = 0;
*counter = 0;
}
}
return false;
}
static int process_pty_loop(int master, sigset_t *mask, pid_t kill_pid, int signo) {
char in_buffer[LINE_MAX], out_buffer[LINE_MAX];
size_t in_buffer_full = 0, out_buffer_full = 0;
struct epoll_event stdin_ev, stdout_ev, master_ev, signal_ev;
bool stdin_readable = false, stdout_writable = false, master_readable = false, master_writable = false;
bool tried_orderly_shutdown = false;
bool stdin_hangup = false, stdout_hangup = false, master_hangup = false;
bool tried_orderly_shutdown = false, process_signalfd = false, quit = false;
usec_t escape_timestamp = 0;
unsigned escape_counter = 0;
_cleanup_close_ int ep = -1, signal_fd = -1;
assert(master >= 0);
@ -103,7 +140,7 @@ int process_pty(int master, sigset_t *mask, pid_t kill_pid, int signo) {
ssize_t k;
int i, nfds;
nfds = epoll_wait(ep, ev, ELEMENTSOF(ev), -1);
nfds = epoll_wait(ep, ev, ELEMENTSOF(ev), quit ? 0 : -1);
if (nfds < 0) {
if (errno == EINTR || errno == EAGAIN)
@ -113,7 +150,8 @@ int process_pty(int master, sigset_t *mask, pid_t kill_pid, int signo) {
return -errno;
}
assert(nfds >= 1);
if (nfds == 0)
return 0;
for (i = 0; i < nfds; i++) {
if (ev[i].data.fd == STDIN_FILENO) {
@ -134,45 +172,8 @@ int process_pty(int master, sigset_t *mask, pid_t kill_pid, int signo) {
if (ev[i].events & (EPOLLOUT|EPOLLHUP))
master_writable = true;
} else if (ev[i].data.fd == signal_fd) {
struct signalfd_siginfo sfsi;
ssize_t n;
n = read(signal_fd, &sfsi, sizeof(sfsi));
if (n != sizeof(sfsi)) {
if (n >= 0) {
log_error("Failed to read from signalfd: invalid block size");
return -EIO;
}
if (errno != EINTR && errno != EAGAIN) {
log_error("Failed to read from signalfd: %m");
return -errno;
}
} else {
if (sfsi.ssi_signo == SIGWINCH) {
struct winsize ws;
/* The window size changed, let's forward that. */
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0)
ioctl(master, TIOCSWINSZ, &ws);
} else if (sfsi.ssi_signo == SIGTERM && kill_pid > 0 && signo > 0 && !tried_orderly_shutdown) {
if (kill(kill_pid, signo) < 0)
return 0;
log_info("Trying to halt container. Send SIGTERM again to trigger immediate termination.");
/* This only works for systemd... */
tried_orderly_shutdown = true;
} else
return 0;
}
}
} else if (ev[i].data.fd == signal_fd)
process_signalfd = true;
}
while ((stdin_readable && in_buffer_full <= 0) ||
@ -185,14 +186,26 @@ int process_pty(int master, sigset_t *mask, pid_t kill_pid, int signo) {
k = read(STDIN_FILENO, in_buffer + in_buffer_full, LINE_MAX - in_buffer_full);
if (k < 0) {
if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
if (errno == EAGAIN)
stdin_readable = false;
else {
else if (errno == EIO || errno == EPIPE || errno == ECONNRESET) {
stdin_readable = false;
stdin_hangup = true;
epoll_ctl(ep, EPOLL_CTL_DEL, STDIN_FILENO, NULL);
} else {
log_error("read(): %m");
return -errno;
}
} else
} else {
/* Check if ^] has been
* pressed three times within
* one second. If we get this
* we quite immediately. */
if (look_for_escape(&escape_timestamp, &escape_counter, in_buffer + in_buffer_full, k))
return !quit;
in_buffer_full += (size_t) k;
}
}
if (master_writable && in_buffer_full > 0) {
@ -200,9 +213,13 @@ int process_pty(int master, sigset_t *mask, pid_t kill_pid, int signo) {
k = write(master, in_buffer, in_buffer_full);
if (k < 0) {
if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
if (errno == EAGAIN || errno == EIO)
master_writable = false;
else {
else if (errno == EPIPE || errno == ECONNRESET) {
master_writable = master_readable = false;
master_hangup = true;
epoll_ctl(ep, EPOLL_CTL_DEL, master, NULL);
} else {
log_error("write(): %m");
return -errno;
}
@ -219,9 +236,21 @@ int process_pty(int master, sigset_t *mask, pid_t kill_pid, int signo) {
k = read(master, out_buffer + out_buffer_full, LINE_MAX - out_buffer_full);
if (k < 0) {
if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
/* Note that EIO on the master
* device might be cause by
* vhangup() or temporary
* closing of everything on
* the other side, we treat it
* like EAGAIN here and try
* again. */
if (errno == EAGAIN || errno == EIO)
master_readable = false;
else {
else if (errno == EPIPE || errno == ECONNRESET) {
master_readable = master_writable = false;
master_hangup = true;
epoll_ctl(ep, EPOLL_CTL_DEL, master, NULL);
} else {
log_error("read(): %m");
return -errno;
}
@ -234,9 +263,13 @@ int process_pty(int master, sigset_t *mask, pid_t kill_pid, int signo) {
k = write(STDOUT_FILENO, out_buffer, out_buffer_full);
if (k < 0) {
if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
if (errno == EAGAIN)
stdout_writable = false;
else {
else if (errno == EIO || errno == EPIPE || errno == ECONNRESET) {
stdout_writable = false;
stdout_hangup = true;
epoll_ctl(ep, EPOLL_CTL_DEL, STDOUT_FILENO, NULL);
} else {
log_error("write(): %m");
return -errno;
}
@ -247,6 +280,91 @@ int process_pty(int master, sigset_t *mask, pid_t kill_pid, int signo) {
out_buffer_full -= k;
}
}
}
if (process_signalfd) {
struct signalfd_siginfo sfsi;
ssize_t n;
n = read(signal_fd, &sfsi, sizeof(sfsi));
if (n != sizeof(sfsi)) {
if (n >= 0) {
log_error("Failed to read from signalfd: invalid block size");
return -EIO;
}
if (errno != EINTR && errno != EAGAIN) {
log_error("Failed to read from signalfd: %m");
return -errno;
}
} else {
if (sfsi.ssi_signo == SIGWINCH) {
struct winsize ws;
/* The window size changed, let's forward that. */
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0)
ioctl(master, TIOCSWINSZ, &ws);
} else if (sfsi.ssi_signo == SIGTERM && kill_pid > 0 && signo > 0 && !tried_orderly_shutdown) {
if (kill(kill_pid, signo) < 0)
quit = true;
else {
log_info("Trying to halt container. Send SIGTERM again to trigger immediate termination.");
/* This only works for systemd... */
tried_orderly_shutdown = true;
}
} else
/* Signals that where
* delivered via signalfd that
* we didn't know are a reason
* for us to quit */
quit = true;
}
}
if (stdin_hangup || stdout_hangup || master_hangup) {
/* Exit the loop if any side hung up and if
* there's nothing more to write or nothing we
* could write. */
if ((out_buffer_full <= 0 || stdout_hangup) &&
(in_buffer_full <= 0 || master_hangup))
return !quit;
}
}
}
int process_pty(int master, sigset_t *mask, pid_t kill_pid, int signo) {
struct termios saved_attr;
bool saved = false;
struct winsize ws;
int r;
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0)
ioctl(master, TIOCSWINSZ, &ws);
if (tcgetattr(STDIN_FILENO, &saved_attr) >= 0) {
struct termios raw_attr;
saved = true;
raw_attr = saved_attr;
cfmakeraw(&raw_attr);
raw_attr.c_lflag &= ~ECHO;
tcsetattr(STDIN_FILENO, TCSANOW, &raw_attr);
}
r = process_pty_loop(master, mask, kill_pid, signo);
if (saved)
tcsetattr(STDIN_FILENO, TCSANOW, &saved_attr);
return r;
}

View File

@ -572,6 +572,10 @@ static inline void umaskp(mode_t *u) {
umask(*u);
}
static inline void close_pipep(int (*p)[2]) {
close_pipe(*p);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, fclose);
DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, pclose);
DEFINE_TRIVIAL_CLEANUP_FUNC(DIR*, closedir);
@ -585,6 +589,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, endmntent);
#define _cleanup_pclose_ _cleanup_(pclosep)
#define _cleanup_closedir_ _cleanup_(closedirp)
#define _cleanup_endmntent_ _cleanup_(endmntentp)
#define _cleanup_close_pipe_ _cleanup_(close_pipep)
_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t a, size_t b) {
if (_unlikely_(b == 0 || a > ((size_t) -1) / b))

2
units/.gitignore vendored
View File

@ -9,6 +9,8 @@
/systemd-suspend.service
/console-getty.service
/console-getty.service.m4
/container-getty@.service
/container-getty@.service.m4
/systemd-journald.service
/user@.service
/systemd-logind.service

View File

@ -15,7 +15,7 @@ After=rc-local.service
Before=getty.target
[Service]
ExecStart=-/sbin/agetty --noclear -s console 115200,38400,9600
ExecStart=-/sbin/agetty --noclear --keep-baud console 115200,38400,9600
Type=idle
Restart=always
RestartSec=0

View File

@ -0,0 +1,29 @@
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Container Getty on /dev/pts/%I
Documentation=man:agetty(8) man:machinectl(1)
After=systemd-user-sessions.service plymouth-quit-wait.service
m4_ifdef(`HAVE_SYSV_COMPAT',
After=rc-local.service
)m4_dnl
Before=getty.target
IgnoreOnIsolate=yes
[Service]
ExecStart=-/sbin/agetty --noclear --keep-baud pts/%I 115200,38400,9600
Type=idle
Restart=always
RestartSec=0
UtmpIdentifier=%I
TTYPath=/dev/%I
TTYReset=yes
TTYVHangup=yes
KillMode=process
IgnoreSIGPIPE=no
SendSIGHUP=yes