machinectl: add new command to spawn a getty inside a container
This commit is contained in:
parent
f85fc84541
commit
04d3927924
|
@ -417,6 +417,7 @@ nodist_systemunit_DATA = \
|
||||||
units/serial-getty@.service \
|
units/serial-getty@.service \
|
||||||
units/console-shell.service \
|
units/console-shell.service \
|
||||||
units/console-getty.service \
|
units/console-getty.service \
|
||||||
|
units/container-getty@.service \
|
||||||
units/systemd-initctl.service \
|
units/systemd-initctl.service \
|
||||||
units/systemd-shutdownd.service \
|
units/systemd-shutdownd.service \
|
||||||
units/systemd-remount-fs.service \
|
units/systemd-remount-fs.service \
|
||||||
|
@ -459,6 +460,7 @@ EXTRA_DIST += \
|
||||||
units/serial-getty@.service.m4 \
|
units/serial-getty@.service.m4 \
|
||||||
units/console-shell.service.m4.in \
|
units/console-shell.service.m4.in \
|
||||||
units/console-getty.service.m4.in \
|
units/console-getty.service.m4.in \
|
||||||
|
units/container-getty@.service.m4.in \
|
||||||
units/rescue.service.m4.in \
|
units/rescue.service.m4.in \
|
||||||
units/systemd-initctl.service.in \
|
units/systemd-initctl.service.in \
|
||||||
units/systemd-shutdownd.service.in \
|
units/systemd-shutdownd.service.in \
|
||||||
|
@ -494,6 +496,7 @@ EXTRA_DIST += \
|
||||||
CLEANFILES += \
|
CLEANFILES += \
|
||||||
units/console-shell.service.m4 \
|
units/console-shell.service.m4 \
|
||||||
units/console-getty.service.m4 \
|
units/console-getty.service.m4 \
|
||||||
|
units/container-getty@.service.m4 \
|
||||||
units/rescue.service.m4
|
units/rescue.service.m4
|
||||||
|
|
||||||
if HAVE_SYSV_COMPAT
|
if HAVE_SYSV_COMPAT
|
||||||
|
|
|
@ -263,6 +263,19 @@
|
||||||
<option>--signal=</option> to select
|
<option>--signal=</option> to select
|
||||||
the signal to send.</para></listitem>
|
the signal to send.</para></listitem>
|
||||||
</varlistentry>
|
</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>
|
</variablelist>
|
||||||
|
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
|
@ -123,10 +123,10 @@
|
||||||
see each other. The PID namespace separation of the
|
see each other. The PID namespace separation of the
|
||||||
two containers is complete and the containers will
|
two containers is complete and the containers will
|
||||||
share very few runtime objects except for the
|
share very few runtime objects except for the
|
||||||
underlying file system. It is however possible to
|
underlying file system. Use
|
||||||
enter an existing container, see
|
<citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
|
||||||
<link linkend='example-nsenter'>Example 4</link> below.
|
<command>login</command> command to request an
|
||||||
</para>
|
additional login prompt in a running container.</para>
|
||||||
|
|
||||||
<para><command>systemd-nspawn</command> implements the
|
<para><command>systemd-nspawn</command> implements the
|
||||||
<ulink
|
<ulink
|
||||||
|
@ -409,24 +409,6 @@
|
||||||
boots an OS in a namespace container in it.</para>
|
boots an OS in a namespace container in it.</para>
|
||||||
</refsect1>
|
</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>
|
<refsect1>
|
||||||
<title>Exit status</title>
|
<title>Exit status</title>
|
||||||
|
|
||||||
|
@ -439,11 +421,11 @@
|
||||||
<para>
|
<para>
|
||||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||||
<citerefentry><refentrytitle>chroot</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>yum</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||||
<citerefentry><refentrytitle>debootstrap</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
<citerefentry><refentrytitle>debootstrap</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||||
<citerefentry><refentrytitle>pacman</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>
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ int bus_container_connect(sd_bus *b) {
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return -ENOMEM;
|
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)
|
if (rootfd < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
|
@ -101,7 +101,6 @@ int bus_container_connect(sd_bus *b) {
|
||||||
if (chroot(".") < 0)
|
if (chroot(".") < 0)
|
||||||
_exit(255);
|
_exit(255);
|
||||||
|
|
||||||
|
|
||||||
r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size);
|
r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
if (errno == EINPROGRESS)
|
if (errno == EINPROGRESS)
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
|
#include <socket.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
#include "sd-bus.h"
|
#include "sd-bus.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
@ -38,6 +40,7 @@
|
||||||
#include "unit-name.h"
|
#include "unit-name.h"
|
||||||
#include "cgroup-show.h"
|
#include "cgroup-show.h"
|
||||||
#include "cgroup-util.h"
|
#include "cgroup-util.h"
|
||||||
|
#include "ptyfwd.h"
|
||||||
|
|
||||||
static char **arg_property = NULL;
|
static char **arg_property = NULL;
|
||||||
static bool arg_all = false;
|
static bool arg_all = false;
|
||||||
|
@ -515,6 +518,239 @@ static int terminate_machine(sd_bus *bus, char **args, unsigned n) {
|
||||||
return 0;
|
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) {
|
static int help(void) {
|
||||||
|
|
||||||
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
|
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
|
||||||
|
@ -535,7 +771,8 @@ static int help(void) {
|
||||||
" status [NAME...] Show VM/container status\n"
|
" status [NAME...] Show VM/container status\n"
|
||||||
" show [NAME...] Show properties of one or more VMs/containers\n"
|
" show [NAME...] Show properties of one or more VMs/containers\n"
|
||||||
" terminate [NAME...] Terminate 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);
|
program_invocation_short_name);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -644,7 +881,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
return 1;
|
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 {
|
static const struct {
|
||||||
const char* verb;
|
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 },
|
{ "show", MORE, 1, show },
|
||||||
{ "terminate", MORE, 2, terminate_machine },
|
{ "terminate", MORE, 2, terminate_machine },
|
||||||
{ "kill", MORE, 2, kill_machine },
|
{ "kill", MORE, 2, kill_machine },
|
||||||
|
{ "login", MORE, 2, login_machine },
|
||||||
};
|
};
|
||||||
|
|
||||||
int left;
|
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.");
|
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);
|
return verbs[i].dispatch(bus, argv + optind, left);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -751,7 +984,7 @@ int main(int argc, char*argv[]) {
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = machinectl_main(bus, argc, argv, r);
|
r = machinectl_main(bus, argc, argv);
|
||||||
ret = r < 0 ? EXIT_FAILURE : r;
|
ret = r < 0 ? EXIT_FAILURE : r;
|
||||||
|
|
||||||
finish:
|
finish:
|
||||||
|
|
|
@ -976,11 +976,8 @@ int main(int argc, char *argv[]) {
|
||||||
_cleanup_close_ int master = -1;
|
_cleanup_close_ int master = -1;
|
||||||
int n_fd_passed;
|
int n_fd_passed;
|
||||||
const char *console = NULL;
|
const char *console = NULL;
|
||||||
struct termios saved_attr, raw_attr;
|
|
||||||
sigset_t mask;
|
sigset_t mask;
|
||||||
bool saved_attr_valid = false;
|
_cleanup_close_pipe_ int kmsg_socket_pair[2] = { -1, -1 };
|
||||||
struct winsize ws;
|
|
||||||
int kmsg_socket_pair[2] = { -1, -1 };
|
|
||||||
_cleanup_fdset_free_ FDSet *fds = NULL;
|
_cleanup_fdset_free_ FDSet *fds = NULL;
|
||||||
|
|
||||||
log_parse_environment();
|
log_parse_environment();
|
||||||
|
@ -1075,24 +1072,13 @@ int main(int argc, char *argv[]) {
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_info("Spawning namespace container on %s (console is %s).", arg_directory, console);
|
log_info("Spawning container %s on %s. Press ^] three times within 1s to abort execution.", arg_machine, arg_directory);
|
||||||
|
|
||||||
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0)
|
|
||||||
ioctl(master, TIOCSWINSZ, &ws);
|
|
||||||
|
|
||||||
if (unlockpt(master) < 0) {
|
if (unlockpt(master) < 0) {
|
||||||
log_error("Failed to unlock tty: %m");
|
log_error("Failed to unlock tty: %m");
|
||||||
goto finish;
|
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) {
|
if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0, kmsg_socket_pair) < 0) {
|
||||||
log_error("Failed to create kmsg socket pair.");
|
log_error("Failed to create kmsg socket pair.");
|
||||||
goto finish;
|
goto finish;
|
||||||
|
@ -1106,18 +1092,6 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
siginfo_t status;
|
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);
|
pid = syscall(__NR_clone, SIGCHLD|CLONE_NEWIPC|CLONE_NEWNS|CLONE_NEWPID|CLONE_NEWUTS|(arg_private_network ? CLONE_NEWNET : 0), NULL);
|
||||||
if (pid < 0) {
|
if (pid < 0) {
|
||||||
|
@ -1152,21 +1126,9 @@ int main(int argc, char *argv[]) {
|
||||||
if (envp[n_env])
|
if (envp[n_env])
|
||||||
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);
|
close_nointr_nofail(master);
|
||||||
master = -1;
|
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(STDIN_FILENO);
|
||||||
close_nointr(STDOUT_FILENO);
|
close_nointr(STDOUT_FILENO);
|
||||||
close_nointr(STDERR_FILENO);
|
close_nointr(STDERR_FILENO);
|
||||||
|
@ -1206,8 +1168,6 @@ int main(int argc, char *argv[]) {
|
||||||
goto child_fail;
|
goto child_fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
close_pipe(pipefd2);
|
|
||||||
|
|
||||||
r = register_machine();
|
r = register_machine();
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto finish;
|
goto finish;
|
||||||
|
@ -1416,25 +1376,23 @@ int main(int argc, char *argv[]) {
|
||||||
_exit(EXIT_FAILURE);
|
_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);
|
fdset_free(fds);
|
||||||
fds = NULL;
|
fds = NULL;
|
||||||
|
|
||||||
if (process_pty(master, &mask, arg_boot ? pid : 0, SIGRTMIN+3) < 0)
|
k = process_pty(master, &mask, arg_boot ? pid : 0, SIGRTMIN+3);
|
||||||
goto finish;
|
if (k < 0) {
|
||||||
|
r = EXIT_FAILURE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (saved_attr_valid)
|
putc('\n', stdout);
|
||||||
tcsetattr(STDIN_FILENO, TCSANOW, &saved_attr);
|
|
||||||
|
/* Kill if it is not dead yet anyway */
|
||||||
|
kill(pid, SIGKILL);
|
||||||
|
|
||||||
k = wait_for_terminate(pid, &status);
|
k = wait_for_terminate(pid, &status);
|
||||||
|
pid = 0;
|
||||||
|
|
||||||
if (k < 0) {
|
if (k < 0) {
|
||||||
r = EXIT_FAILURE;
|
r = EXIT_FAILURE;
|
||||||
break;
|
break;
|
||||||
|
@ -1443,40 +1401,35 @@ int main(int argc, char *argv[]) {
|
||||||
if (status.si_code == CLD_EXITED) {
|
if (status.si_code == CLD_EXITED) {
|
||||||
r = status.si_status;
|
r = status.si_status;
|
||||||
if (status.si_status != 0) {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_debug("Container exited successfully.");
|
log_debug("Container %s exited successfully.", arg_machine);
|
||||||
break;
|
break;
|
||||||
} else if (status.si_code == CLD_KILLED &&
|
} else if (status.si_code == CLD_KILLED &&
|
||||||
status.si_status == SIGINT) {
|
status.si_status == SIGINT) {
|
||||||
log_info("Container has been shut down.");
|
log_info("Container %s has been shut down.", arg_machine);
|
||||||
r = 0;
|
r = 0;
|
||||||
break;
|
break;
|
||||||
} else if (status.si_code == CLD_KILLED &&
|
} else if (status.si_code == CLD_KILLED &&
|
||||||
status.si_status == SIGHUP) {
|
status.si_status == SIGHUP) {
|
||||||
log_info("Container is being rebooted.");
|
log_info("Container %s is being rebooted.", arg_machine);
|
||||||
continue;
|
continue;
|
||||||
} else if (status.si_code == CLD_KILLED ||
|
} else if (status.si_code == CLD_KILLED ||
|
||||||
status.si_code == CLD_DUMPED) {
|
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;
|
r = EXIT_FAILURE;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
log_error("Container failed due to unknown reason.");
|
log_error("Container %s failed due to unknown reason.", arg_machine);
|
||||||
r = EXIT_FAILURE;
|
r = EXIT_FAILURE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
finish:
|
finish:
|
||||||
if (saved_attr_valid)
|
|
||||||
tcsetattr(STDIN_FILENO, TCSANOW, &saved_attr);
|
|
||||||
|
|
||||||
close_pipe(kmsg_socket_pair);
|
|
||||||
|
|
||||||
if (pid > 0)
|
if (pid > 0)
|
||||||
kill(pid, SIGKILL);
|
kill(pid, SIGKILL);
|
||||||
|
|
||||||
|
|
|
@ -28,12 +28,49 @@
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "ptyfwd.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];
|
char in_buffer[LINE_MAX], out_buffer[LINE_MAX];
|
||||||
size_t in_buffer_full = 0, out_buffer_full = 0;
|
size_t in_buffer_full = 0, out_buffer_full = 0;
|
||||||
struct epoll_event stdin_ev, stdout_ev, master_ev, signal_ev;
|
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 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;
|
_cleanup_close_ int ep = -1, signal_fd = -1;
|
||||||
|
|
||||||
assert(master >= 0);
|
assert(master >= 0);
|
||||||
|
@ -103,7 +140,7 @@ int process_pty(int master, sigset_t *mask, pid_t kill_pid, int signo) {
|
||||||
ssize_t k;
|
ssize_t k;
|
||||||
int i, nfds;
|
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 (nfds < 0) {
|
||||||
|
|
||||||
if (errno == EINTR || errno == EAGAIN)
|
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;
|
return -errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(nfds >= 1);
|
if (nfds == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
for (i = 0; i < nfds; i++) {
|
for (i = 0; i < nfds; i++) {
|
||||||
if (ev[i].data.fd == STDIN_FILENO) {
|
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))
|
if (ev[i].events & (EPOLLOUT|EPOLLHUP))
|
||||||
master_writable = true;
|
master_writable = true;
|
||||||
|
|
||||||
} else if (ev[i].data.fd == signal_fd) {
|
} else if (ev[i].data.fd == signal_fd)
|
||||||
struct signalfd_siginfo sfsi;
|
process_signalfd = true;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((stdin_readable && in_buffer_full <= 0) ||
|
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);
|
k = read(STDIN_FILENO, in_buffer + in_buffer_full, LINE_MAX - in_buffer_full);
|
||||||
if (k < 0) {
|
if (k < 0) {
|
||||||
|
|
||||||
if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
|
if (errno == EAGAIN)
|
||||||
stdin_readable = false;
|
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");
|
log_error("read(): %m");
|
||||||
return -errno;
|
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;
|
in_buffer_full += (size_t) k;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (master_writable && in_buffer_full > 0) {
|
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);
|
k = write(master, in_buffer, in_buffer_full);
|
||||||
if (k < 0) {
|
if (k < 0) {
|
||||||
|
|
||||||
if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
|
if (errno == EAGAIN || errno == EIO)
|
||||||
master_writable = false;
|
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");
|
log_error("write(): %m");
|
||||||
return -errno;
|
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);
|
k = read(master, out_buffer + out_buffer_full, LINE_MAX - out_buffer_full);
|
||||||
if (k < 0) {
|
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;
|
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");
|
log_error("read(): %m");
|
||||||
return -errno;
|
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);
|
k = write(STDOUT_FILENO, out_buffer, out_buffer_full);
|
||||||
if (k < 0) {
|
if (k < 0) {
|
||||||
|
|
||||||
if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
|
if (errno == EAGAIN)
|
||||||
stdout_writable = false;
|
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");
|
log_error("write(): %m");
|
||||||
return -errno;
|
return -errno;
|
||||||
}
|
}
|
||||||
|
@ -247,6 +280,91 @@ int process_pty(int master, sigset_t *mask, pid_t kill_pid, int signo) {
|
||||||
out_buffer_full -= k;
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -572,6 +572,10 @@ static inline void umaskp(mode_t *u) {
|
||||||
umask(*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*, fclose);
|
||||||
DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, pclose);
|
DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, pclose);
|
||||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DIR*, closedir);
|
DEFINE_TRIVIAL_CLEANUP_FUNC(DIR*, closedir);
|
||||||
|
@ -585,6 +589,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, endmntent);
|
||||||
#define _cleanup_pclose_ _cleanup_(pclosep)
|
#define _cleanup_pclose_ _cleanup_(pclosep)
|
||||||
#define _cleanup_closedir_ _cleanup_(closedirp)
|
#define _cleanup_closedir_ _cleanup_(closedirp)
|
||||||
#define _cleanup_endmntent_ _cleanup_(endmntentp)
|
#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) {
|
_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t a, size_t b) {
|
||||||
if (_unlikely_(b == 0 || a > ((size_t) -1) / b))
|
if (_unlikely_(b == 0 || a > ((size_t) -1) / b))
|
||||||
|
|
2
units/.gitignore
vendored
2
units/.gitignore
vendored
|
@ -9,6 +9,8 @@
|
||||||
/systemd-suspend.service
|
/systemd-suspend.service
|
||||||
/console-getty.service
|
/console-getty.service
|
||||||
/console-getty.service.m4
|
/console-getty.service.m4
|
||||||
|
/container-getty@.service
|
||||||
|
/container-getty@.service.m4
|
||||||
/systemd-journald.service
|
/systemd-journald.service
|
||||||
/user@.service
|
/user@.service
|
||||||
/systemd-logind.service
|
/systemd-logind.service
|
||||||
|
|
|
@ -15,7 +15,7 @@ After=rc-local.service
|
||||||
Before=getty.target
|
Before=getty.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=-/sbin/agetty --noclear -s console 115200,38400,9600
|
ExecStart=-/sbin/agetty --noclear --keep-baud console 115200,38400,9600
|
||||||
Type=idle
|
Type=idle
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=0
|
RestartSec=0
|
||||||
|
|
29
units/container-getty@.service.m4.in
Normal file
29
units/container-getty@.service.m4.in
Normal 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
|
Loading…
Reference in a new issue