[systemd-commits] 3 commits - Makefile.am TODO man/machinectl.xml man/systemd-nspawn.xml src/getty-generator src/libsystemd-bus src/machine src/nspawn src/shared units/.gitignore units/console-getty.service.m4.in units/container-getty at .service.m4.in

Lennart Poettering lennart at kemper.freedesktop.org
Thu Oct 31 01:43:53 CET 2013


 Makefile.am                           |    7 
 TODO                                  |    2 
 man/machinectl.xml                    |   13 +
 man/systemd-nspawn.xml                |   30 --
 src/getty-generator/getty-generator.c |   86 ++-----
 src/libsystemd-bus/bus-container.c    |    3 
 src/machine/machinectl.c              |  249 ++++++++++++++++++++++
 src/nspawn/nspawn.c                   |  328 +-----------------------------
 src/shared/ptyfwd.c                   |  370 ++++++++++++++++++++++++++++++++++
 src/shared/ptyfwd.h                   |   27 ++
 src/shared/util.h                     |    5 
 units/.gitignore                      |    2 
 units/console-getty.service.m4.in     |    2 
 units/container-getty at .service.m4.in  |   29 ++
 14 files changed, 751 insertions(+), 402 deletions(-)

New commits:
commit 04d39279245834494baccfdb9349db8bf80abd13
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Oct 31 01:25:44 2013 +0100

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

diff --git a/Makefile.am b/Makefile.am
index cc29d73..630fb19 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -417,6 +417,7 @@ nodist_systemunit_DATA = \
 	units/serial-getty at .service \
 	units/console-shell.service \
 	units/console-getty.service \
+	units/container-getty at .service \
 	units/systemd-initctl.service \
 	units/systemd-shutdownd.service \
 	units/systemd-remount-fs.service \
@@ -459,6 +460,7 @@ EXTRA_DIST += \
 	units/serial-getty at .service.m4 \
 	units/console-shell.service.m4.in \
 	units/console-getty.service.m4.in \
+	units/container-getty at .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 at .service.m4 \
 	units/rescue.service.m4
 
 if HAVE_SYSV_COMPAT
diff --git a/man/machinectl.xml b/man/machinectl.xml
index f2fa6ce..c06d0c7 100644
--- a/man/machinectl.xml
+++ b/man/machinectl.xml
@@ -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>
diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml
index 7d450f9..c2be6d5 100644
--- a/man/systemd-nspawn.xml
+++ b/man/systemd-nspawn.xml
@@ -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>
 
diff --git a/src/libsystemd-bus/bus-container.c b/src/libsystemd-bus/bus-container.c
index 31bb624..25ea471 100644
--- a/src/libsystemd-bus/bus-container.c
+++ b/src/libsystemd-bus/bus-container.c
@@ -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)
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index f43d481..7a252c3 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -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:
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index cb2f05c..1477456 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -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);
 
diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c
index 1e2852b..7225b93 100644
--- a/src/shared/ptyfwd.c
+++ b/src/shared/ptyfwd.c
@@ -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;
+
+}
diff --git a/src/shared/util.h b/src/shared/util.h
index dc35b4d..d90b808 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -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))
diff --git a/units/.gitignore b/units/.gitignore
index 6a67f3f..5fd9cca 100644
--- a/units/.gitignore
+++ b/units/.gitignore
@@ -9,6 +9,8 @@
 /systemd-suspend.service
 /console-getty.service
 /console-getty.service.m4
+/container-getty at .service
+/container-getty at .service.m4
 /systemd-journald.service
 /user at .service
 /systemd-logind.service
diff --git a/units/console-getty.service.m4.in b/units/console-getty.service.m4.in
index 2fd9dd8..464732c 100644
--- a/units/console-getty.service.m4.in
+++ b/units/console-getty.service.m4.in
@@ -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
diff --git a/units/container-getty at .service.m4.in b/units/container-getty at .service.m4.in
new file mode 100644
index 0000000..abc58cb
--- /dev/null
+++ b/units/container-getty at .service.m4.in
@@ -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

commit f85fc8454137a5cfba99b3a24aaa6dc38f6e2768
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Oct 30 20:19:22 2013 +0100

    getty-generator: modernizations

diff --git a/src/getty-generator/getty-generator.c b/src/getty-generator/getty-generator.c
index 6c93806..0dc407d 100644
--- a/src/getty-generator/getty-generator.c
+++ b/src/getty-generator/getty-generator.c
@@ -33,19 +33,19 @@
 static const char *arg_dest = "/tmp";
 
 static int add_symlink(const char *fservice, const char *tservice) {
-        char *from = NULL, *to = NULL;
+        _cleanup_free_ char *from = NULL, *to = NULL;
         int r;
 
         assert(fservice);
         assert(tservice);
 
         from = strappend(SYSTEM_DATA_UNIT_PATH "/", fservice);
-        to = strjoin(arg_dest,"/getty.target.wants/", tservice, NULL);
+        if (!from)
+                return log_oom();
 
-        if (!from || !to) {
-                r = log_oom();
-                goto finish;
-        }
+        to = strjoin(arg_dest,"/getty.target.wants/", tservice, NULL);
+        if (!to)
+                return log_oom();
 
         mkdir_parents_label(to, 0755);
 
@@ -53,24 +53,18 @@ static int add_symlink(const char *fservice, const char *tservice) {
         if (r < 0) {
                 if (errno == EEXIST)
                         /* In case console=hvc0 is passed this will very likely result in EEXIST */
-                        r = 0;
+                        return 0;
                 else {
                         log_error("Failed to create symlink %s: %m", to);
-                        r = -errno;
+                        return -errno;
                 }
         }
 
-finish:
-
-        free(from);
-        free(to);
-
-        return r;
+        return 0;
 }
 
 static int add_serial_getty(const char *tty) {
-        char *n;
-        int r;
+        _cleanup_free_ char *n = NULL;
 
         assert(tty);
 
@@ -80,10 +74,7 @@ static int add_serial_getty(const char *tty) {
         if (!n)
                 return log_oom();
 
-        r = add_symlink("serial-getty at .service", n);
-        free(n);
-
-        return r;
+        return add_symlink("serial-getty at .service", n);
 }
 
 int main(int argc, char *argv[]) {
@@ -93,8 +84,7 @@ int main(int argc, char *argv[]) {
                 "xvc0\0"
                 "hvsi0\0";
 
-        int r = EXIT_SUCCESS;
-        char *active;
+        _cleanup_free_ char *active = NULL;
         const char *j;
 
         if (argc > 1 && argc != 4) {
@@ -115,10 +105,10 @@ int main(int argc, char *argv[]) {
                 log_debug("Automatically adding console shell.");
 
                 if (add_symlink("console-getty.service", "console-getty.service") < 0)
-                        r = EXIT_FAILURE;
+                        return EXIT_FAILURE;
 
                 /* Don't add any further magic if we are in a container */
-                goto finish;
+                return EXIT_SUCCESS;
         }
 
         if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) {
@@ -128,63 +118,43 @@ int main(int argc, char *argv[]) {
                 /* Automatically add in a serial getty on all active
                  * kernel consoles */
                 FOREACH_WORD(w, l, active, state) {
-                        char *tty;
-                        int k;
+                        _cleanup_free_ char *tty = NULL;
 
                         tty = strndup(w, l);
                         if (!tty) {
-                            log_oom();
-                            free(active);
-                            r = EXIT_FAILURE;
-                            goto finish;
+                                log_oom();
+                                return EXIT_FAILURE;
                         }
 
-                        if (isempty(tty) || tty_is_vc(tty)) {
-                                free(tty);
+                        if (isempty(tty) || tty_is_vc(tty))
                                 continue;
-                        }
 
                         /* We assume that gettys on virtual terminals are
                          * started via manual configuration and do this magic
                          * only for non-VC terminals. */
 
-                        k = add_serial_getty(tty);
-
-                        if (k < 0) {
-                                free(tty);
-                                free(active);
-                                r = EXIT_FAILURE;
-                                goto finish;
-                        }
+                        if (add_serial_getty(tty) < 0)
+                                return EXIT_FAILURE;
                 }
-                free(active);
         }
 
         /* Automatically add in a serial getty on the first
          * virtualizer console */
         NULSTR_FOREACH(j, virtualization_consoles) {
-                char *p;
-                int k;
+                _cleanup_free_ char *p = NULL;
 
-                if (asprintf(&p, "/sys/class/tty/%s", j) < 0) {
+                p = strappend("/sys/class/tty/", j);
+                if (!p) {
                         log_oom();
-                        r = EXIT_FAILURE;
-                        goto finish;
+                        return EXIT_FAILURE;
                 }
 
-                k = access(p, F_OK);
-                free(p);
-
-                if (k < 0)
+                if (access(p, F_OK) < 0)
                         continue;
 
-                k = add_serial_getty(j);
-                if (k < 0) {
-                        r = EXIT_FAILURE;
-                        goto finish;
-                }
+                if (add_serial_getty(j) < 0)
+                        return EXIT_FAILURE;
         }
 
-finish:
-        return r;
+        return EXIT_SUCCESS;
 }

commit 4ba93280223ceb5de1bcedb196c38252f334521a
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Oct 30 18:58:59 2013 +0100

    nspawn: split out pty forwaring logic into ptyfwd.c

diff --git a/Makefile.am b/Makefile.am
index 4810150..cc29d73 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -744,7 +744,9 @@ libsystemd_shared_la_SOURCES = \
 	src/shared/apparmor-util.c \
 	src/shared/apparmor-util.h \
 	src/shared/ima-util.c \
-	src/shared/ima-util.h
+	src/shared/ima-util.h \
+	src/shared/ptyfwd.c \
+	src/shared/ptyfwd.h
 
 #-------------------------------------------------------------------------------
 noinst_LTLIBRARIES += \
diff --git a/TODO b/TODO
index ef7c6cd..c537ff0 100644
--- a/TODO
+++ b/TODO
@@ -48,6 +48,8 @@ CGroup Rework Completion:
 
 Features:
 
+* remove NSS usage from PID 1 (notably the specifiers)
+
 * socket-proxyd:
   - Support multiple inherited sockets mapped using different proxies
   - Use a nonblocking alternative to getaddrinfo
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index c0f9948..cb2f05c 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -62,6 +62,7 @@
 #include "build.h"
 #include "fileio.h"
 #include "bus-util.h"
+#include "ptyfwd.h"
 
 #ifndef TTY_GID
 #define TTY_GID 5
@@ -923,248 +924,6 @@ static int drop_capabilities(void) {
         return capability_bounding_set_drop(~arg_retain, false);
 }
 
-static int process_pty(int master, pid_t pid, sigset_t *mask) {
-
-        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;
-        int ep = -1, signal_fd = -1, r;
-        bool tried_orderly_shutdown = false;
-
-        assert(master >= 0);
-        assert(pid > 0);
-        assert(mask);
-
-        fd_nonblock(STDIN_FILENO, 1);
-        fd_nonblock(STDOUT_FILENO, 1);
-        fd_nonblock(master, 1);
-
-        signal_fd = signalfd(-1, mask, SFD_NONBLOCK|SFD_CLOEXEC);
-        if (signal_fd < 0) {
-                log_error("signalfd(): %m");
-                r = -errno;
-                goto finish;
-        }
-
-        ep = epoll_create1(EPOLL_CLOEXEC);
-        if (ep < 0) {
-                log_error("Failed to create epoll: %m");
-                r = -errno;
-                goto finish;
-        }
-
-        /* We read from STDIN only if this is actually a TTY,
-         * otherwise we assume non-interactivity. */
-        if (isatty(STDIN_FILENO)) {
-                zero(stdin_ev);
-                stdin_ev.events = EPOLLIN|EPOLLET;
-                stdin_ev.data.fd = STDIN_FILENO;
-
-                if (epoll_ctl(ep, EPOLL_CTL_ADD, STDIN_FILENO, &stdin_ev) < 0) {
-                        log_error("Failed to register STDIN in epoll: %m");
-                        r = -errno;
-                        goto finish;
-                }
-        }
-
-        zero(stdout_ev);
-        stdout_ev.events = EPOLLOUT|EPOLLET;
-        stdout_ev.data.fd = STDOUT_FILENO;
-
-        zero(master_ev);
-        master_ev.events = EPOLLIN|EPOLLOUT|EPOLLET;
-        master_ev.data.fd = master;
-
-        zero(signal_ev);
-        signal_ev.events = EPOLLIN;
-        signal_ev.data.fd = signal_fd;
-
-        if (epoll_ctl(ep, EPOLL_CTL_ADD, STDOUT_FILENO, &stdout_ev) < 0) {
-                if (errno != EPERM) {
-                        log_error("Failed to register stdout in epoll: %m");
-                        r = -errno;
-                        goto finish;
-                }
-                /* stdout without epoll support. Likely redirected to regular file. */
-                stdout_writable = true;
-        }
-
-        if (epoll_ctl(ep, EPOLL_CTL_ADD, master, &master_ev) < 0 ||
-            epoll_ctl(ep, EPOLL_CTL_ADD, signal_fd, &signal_ev) < 0) {
-                log_error("Failed to register fds in epoll: %m");
-                r = -errno;
-                goto finish;
-        }
-
-        for (;;) {
-                struct epoll_event ev[16];
-                ssize_t k;
-                int i, nfds;
-
-                nfds = epoll_wait(ep, ev, ELEMENTSOF(ev), -1);
-                if (nfds < 0) {
-
-                        if (errno == EINTR || errno == EAGAIN)
-                                continue;
-
-                        log_error("epoll_wait(): %m");
-                        r = -errno;
-                        goto finish;
-                }
-
-                assert(nfds >= 1);
-
-                for (i = 0; i < nfds; i++) {
-                        if (ev[i].data.fd == STDIN_FILENO) {
-
-                                if (ev[i].events & (EPOLLIN|EPOLLHUP))
-                                        stdin_readable = true;
-
-                        } else if (ev[i].data.fd == STDOUT_FILENO) {
-
-                                if (ev[i].events & (EPOLLOUT|EPOLLHUP))
-                                        stdout_writable = true;
-
-                        } else if (ev[i].data.fd == master) {
-
-                                if (ev[i].events & (EPOLLIN|EPOLLHUP))
-                                        master_readable = true;
-
-                                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");
-                                                r = -EIO;
-                                                goto finish;
-                                        }
-
-                                        if (errno != EINTR && errno != EAGAIN) {
-                                                log_error("Failed to read from signalfd: %m");
-                                                r = -errno;
-                                                goto finish;
-                                        }
-                                } 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 && arg_boot && !tried_orderly_shutdown) {
-
-                                                log_info("Trying to halt container. Send SIGTERM again to trigger immediate termination.");
-
-                                                /* This only works for systemd... */
-                                                tried_orderly_shutdown = true;
-                                                kill(pid, SIGRTMIN+3);
-
-                                        } else {
-                                                r = 0;
-                                                goto finish;
-                                        }
-                                }
-                        }
-                }
-
-                while ((stdin_readable && in_buffer_full <= 0) ||
-                       (master_writable && in_buffer_full > 0) ||
-                       (master_readable && out_buffer_full <= 0) ||
-                       (stdout_writable && out_buffer_full > 0)) {
-
-                        if (stdin_readable && in_buffer_full < LINE_MAX) {
-
-                                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)
-                                                stdin_readable = false;
-                                        else {
-                                                log_error("read(): %m");
-                                                r = -errno;
-                                                goto finish;
-                                        }
-                                } else
-                                        in_buffer_full += (size_t) k;
-                        }
-
-                        if (master_writable && in_buffer_full > 0) {
-
-                                k = write(master, in_buffer, in_buffer_full);
-                                if (k < 0) {
-
-                                        if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
-                                                master_writable = false;
-                                        else {
-                                                log_error("write(): %m");
-                                                r = -errno;
-                                                goto finish;
-                                        }
-
-                                } else {
-                                        assert(in_buffer_full >= (size_t) k);
-                                        memmove(in_buffer, in_buffer + k, in_buffer_full - k);
-                                        in_buffer_full -= k;
-                                }
-                        }
-
-                        if (master_readable && out_buffer_full < LINE_MAX) {
-
-                                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)
-                                                master_readable = false;
-                                        else {
-                                                log_error("read(): %m");
-                                                r = -errno;
-                                                goto finish;
-                                        }
-                                }  else
-                                        out_buffer_full += (size_t) k;
-                        }
-
-                        if (stdout_writable && out_buffer_full > 0) {
-
-                                k = write(STDOUT_FILENO, out_buffer, out_buffer_full);
-                                if (k < 0) {
-
-                                        if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
-                                                stdout_writable = false;
-                                        else {
-                                                log_error("write(): %m");
-                                                r = -errno;
-                                                goto finish;
-                                        }
-
-                                } else {
-                                        assert(out_buffer_full >= (size_t) k);
-                                        memmove(out_buffer, out_buffer + k, out_buffer_full - k);
-                                        out_buffer_full -= k;
-                                }
-                        }
-                }
-        }
-
-finish:
-        if (ep >= 0)
-                close_nointr_nofail(ep);
-
-        if (signal_fd >= 0)
-                close_nointr_nofail(signal_fd);
-
-        return r;
-}
-
 static int register_machine(void) {
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_bus_unref_ sd_bus *bus = NULL;
@@ -1669,7 +1428,7 @@ int main(int argc, char *argv[]) {
                 fdset_free(fds);
                 fds = NULL;
 
-                if (process_pty(master, pid, &mask) < 0)
+                if (process_pty(master, &mask, arg_boot ? pid : 0, SIGRTMIN+3) < 0)
                         goto finish;
 
                 if (saved_attr_valid)
diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c
new file mode 100644
index 0000000..1e2852b
--- /dev/null
+++ b/src/shared/ptyfwd.c
@@ -0,0 +1,252 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010-2013 Lennart Poettering
+
+  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.
+
+  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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/epoll.h>
+#include <sys/signalfd.h>
+#include <sys/ioctl.h>
+#include <limits.h>
+#include <termios.h>
+
+#include "util.h"
+#include "ptyfwd.h"
+
+int process_pty(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;
+        _cleanup_close_ int ep = -1, signal_fd = -1;
+
+        assert(master >= 0);
+        assert(mask);
+        assert(kill_pid == 0 || kill_pid > 1);
+        assert(signo >= 0 && signo < _NSIG);
+
+        fd_nonblock(STDIN_FILENO, 1);
+        fd_nonblock(STDOUT_FILENO, 1);
+        fd_nonblock(master, 1);
+
+        signal_fd = signalfd(-1, mask, SFD_NONBLOCK|SFD_CLOEXEC);
+        if (signal_fd < 0) {
+                log_error("signalfd(): %m");
+                return -errno;
+        }
+
+        ep = epoll_create1(EPOLL_CLOEXEC);
+        if (ep < 0) {
+                log_error("Failed to create epoll: %m");
+                return -errno;
+        }
+
+        /* We read from STDIN only if this is actually a TTY,
+         * otherwise we assume non-interactivity. */
+        if (isatty(STDIN_FILENO)) {
+                zero(stdin_ev);
+                stdin_ev.events = EPOLLIN|EPOLLET;
+                stdin_ev.data.fd = STDIN_FILENO;
+
+                if (epoll_ctl(ep, EPOLL_CTL_ADD, STDIN_FILENO, &stdin_ev) < 0) {
+                        log_error("Failed to register STDIN in epoll: %m");
+                        return -errno;
+                }
+        }
+
+        zero(stdout_ev);
+        stdout_ev.events = EPOLLOUT|EPOLLET;
+        stdout_ev.data.fd = STDOUT_FILENO;
+
+        zero(master_ev);
+        master_ev.events = EPOLLIN|EPOLLOUT|EPOLLET;
+        master_ev.data.fd = master;
+
+        zero(signal_ev);
+        signal_ev.events = EPOLLIN;
+        signal_ev.data.fd = signal_fd;
+
+        if (epoll_ctl(ep, EPOLL_CTL_ADD, STDOUT_FILENO, &stdout_ev) < 0) {
+                if (errno != EPERM) {
+                        log_error("Failed to register stdout in epoll: %m");
+                        return -errno;
+                }
+
+                /* stdout without epoll support. Likely redirected to regular file. */
+                stdout_writable = true;
+        }
+
+        if (epoll_ctl(ep, EPOLL_CTL_ADD, master, &master_ev) < 0 ||
+            epoll_ctl(ep, EPOLL_CTL_ADD, signal_fd, &signal_ev) < 0) {
+                log_error("Failed to register fds in epoll: %m");
+                return -errno;
+        }
+
+        for (;;) {
+                struct epoll_event ev[16];
+                ssize_t k;
+                int i, nfds;
+
+                nfds = epoll_wait(ep, ev, ELEMENTSOF(ev), -1);
+                if (nfds < 0) {
+
+                        if (errno == EINTR || errno == EAGAIN)
+                                continue;
+
+                        log_error("epoll_wait(): %m");
+                        return -errno;
+                }
+
+                assert(nfds >= 1);
+
+                for (i = 0; i < nfds; i++) {
+                        if (ev[i].data.fd == STDIN_FILENO) {
+
+                                if (ev[i].events & (EPOLLIN|EPOLLHUP))
+                                        stdin_readable = true;
+
+                        } else if (ev[i].data.fd == STDOUT_FILENO) {
+
+                                if (ev[i].events & (EPOLLOUT|EPOLLHUP))
+                                        stdout_writable = true;
+
+                        } else if (ev[i].data.fd == master) {
+
+                                if (ev[i].events & (EPOLLIN|EPOLLHUP))
+                                        master_readable = true;
+
+                                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;
+                                }
+                        }
+                }
+
+                while ((stdin_readable && in_buffer_full <= 0) ||
+                       (master_writable && in_buffer_full > 0) ||
+                       (master_readable && out_buffer_full <= 0) ||
+                       (stdout_writable && out_buffer_full > 0)) {
+
+                        if (stdin_readable && in_buffer_full < LINE_MAX) {
+
+                                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)
+                                                stdin_readable = false;
+                                        else {
+                                                log_error("read(): %m");
+                                                return -errno;
+                                        }
+                                } else
+                                        in_buffer_full += (size_t) k;
+                        }
+
+                        if (master_writable && in_buffer_full > 0) {
+
+                                k = write(master, in_buffer, in_buffer_full);
+                                if (k < 0) {
+
+                                        if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
+                                                master_writable = false;
+                                        else {
+                                                log_error("write(): %m");
+                                                return -errno;
+                                        }
+
+                                } else {
+                                        assert(in_buffer_full >= (size_t) k);
+                                        memmove(in_buffer, in_buffer + k, in_buffer_full - k);
+                                        in_buffer_full -= k;
+                                }
+                        }
+
+                        if (master_readable && out_buffer_full < LINE_MAX) {
+
+                                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)
+                                                master_readable = false;
+                                        else {
+                                                log_error("read(): %m");
+                                                return -errno;
+                                        }
+                                }  else
+                                        out_buffer_full += (size_t) k;
+                        }
+
+                        if (stdout_writable && out_buffer_full > 0) {
+
+                                k = write(STDOUT_FILENO, out_buffer, out_buffer_full);
+                                if (k < 0) {
+
+                                        if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
+                                                stdout_writable = false;
+                                        else {
+                                                log_error("write(): %m");
+                                                return -errno;
+                                        }
+
+                                } else {
+                                        assert(out_buffer_full >= (size_t) k);
+                                        memmove(out_buffer, out_buffer + k, out_buffer_full - k);
+                                        out_buffer_full -= k;
+                                }
+                        }
+                }
+        }
+}
diff --git a/src/shared/ptyfwd.h b/src/shared/ptyfwd.h
new file mode 100644
index 0000000..8b65702
--- /dev/null
+++ b/src/shared/ptyfwd.h
@@ -0,0 +1,27 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010-2013 Lennart Poettering
+
+  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.
+
+  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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+#include <signal.h>
+
+int process_pty(int master, sigset_t *mask, pid_t kill_pid, int signo);



More information about the systemd-commits mailing list