chiark / gitweb /
machinectl: add new command to spawn a getty inside a container
authorLennart Poettering <lennart@poettering.net>
Thu, 31 Oct 2013 00:25:44 +0000 (01:25 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 31 Oct 2013 00:43:38 +0000 (01:43 +0100)
Makefile.am
man/machinectl.xml
man/systemd-nspawn.xml
src/libsystemd-bus/bus-container.c
src/machine/machinectl.c
src/nspawn/nspawn.c
src/shared/ptyfwd.c
src/shared/util.h
units/.gitignore
units/console-getty.service.m4.in
units/container-getty@.service.m4.in [new file with mode: 0644]

index cc29d73a773282d85b038597fb85c633b992fff2..630fb19c3545289ea8b2065ce2cc91560a610085 100644 (file)
@@ -417,6 +417,7 @@ nodist_systemunit_DATA = \
        units/serial-getty@.service \
        units/console-shell.service \
        units/console-getty.service \
        units/serial-getty@.service \
        units/console-shell.service \
        units/console-getty.service \
+       units/container-getty@.service \
        units/systemd-initctl.service \
        units/systemd-shutdownd.service \
        units/systemd-remount-fs.service \
        units/systemd-initctl.service \
        units/systemd-shutdownd.service \
        units/systemd-remount-fs.service \
@@ -459,6 +460,7 @@ EXTRA_DIST += \
        units/serial-getty@.service.m4 \
        units/console-shell.service.m4.in \
        units/console-getty.service.m4.in \
        units/serial-getty@.service.m4 \
        units/console-shell.service.m4.in \
        units/console-getty.service.m4.in \
+       units/container-getty@.service.m4.in \
        units/rescue.service.m4.in \
        units/systemd-initctl.service.in \
        units/systemd-shutdownd.service.in \
        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 \
 CLEANFILES += \
        units/console-shell.service.m4 \
        units/console-getty.service.m4 \
+       units/container-getty@.service.m4 \
        units/rescue.service.m4
 
 if HAVE_SYSV_COMPAT
        units/rescue.service.m4
 
 if HAVE_SYSV_COMPAT
index f2fa6ce53e9aff3f2b974683ec671dec717e4798..c06d0c7abb9def6720b4619c533787ca9c597df8 100644 (file)
                                 <option>--signal=</option> to select
                                 the signal to send.</para></listitem>
                         </varlistentry>
                                 <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>
                 </variablelist>
 
         </refsect1>
index 7d450f912c1c617c17d6bd41a32eb0d87374963a..c2be6d5446babde16ca2ddb4dbc169d65cd5ff2e 100644 (file)
                 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
                 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
 
                 <para><command>systemd-nspawn</command> implements the
                 <ulink
                 boots an OS in a namespace container in it.</para>
         </refsect1>
 
                 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>
 
         <refsect1>
                 <title>Exit status</title>
 
                 <para>
                         <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
                         <citerefentry><refentrytitle>chroot</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
                 <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>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>
 
                 </para>
         </refsect1>
 
index 31bb624b34cd4dca37cac8c063d8fa8b7c222e86..25ea471a08475085d00b0c2166f554a02e429b18 100644 (file)
@@ -72,7 +72,7 @@ int bus_container_connect(sd_bus *b) {
         if (r < 0)
                 return -ENOMEM;
 
         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;
 
         if (rootfd < 0)
                 return -errno;
 
@@ -101,7 +101,6 @@ int bus_container_connect(sd_bus *b) {
                 if (chroot(".") < 0)
                         _exit(255);
 
                 if (chroot(".") < 0)
                         _exit(255);
 
-
                 r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size);
                 if (r < 0) {
                         if (errno == EINPROGRESS)
                 r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size);
                 if (r < 0) {
                         if (errno == EINPROGRESS)
index f43d4814d1fe70253821dd7aef57fd94a443ff16..7a252c34aab02d3b59e79f3d3cb944329175c44a 100644 (file)
@@ -25,6 +25,8 @@
 #include <getopt.h>
 #include <pwd.h>
 #include <locale.h>
 #include <getopt.h>
 #include <pwd.h>
 #include <locale.h>
+#include <socket.h>
+#include <fcntl.h>
 
 #include "sd-bus.h"
 #include "log.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 "unit-name.h"
 #include "cgroup-show.h"
 #include "cgroup-util.h"
+#include "ptyfwd.h"
 
 static char **arg_property = NULL;
 static bool arg_all = false;
 
 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;
 }
 
         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"
 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"
                "  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;
                program_invocation_short_name);
 
         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 {
                 const char* verb;
 
         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      },
                 { "show",                  MORE,   1, show              },
                 { "terminate",             MORE,   2, terminate_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;
 
 finish:
         ret = r < 0 ? EXIT_FAILURE : r;
 
 finish:
index cb2f05c02d0c74c1cce1d4db64dce29840c70605..14774566a64dfe2c5f71946f88855a5c136003ad 100644 (file)
@@ -976,11 +976,8 @@ int main(int argc, char *argv[]) {
         _cleanup_close_ int master = -1;
         int n_fd_passed;
         const char *console = NULL;
         _cleanup_close_ int master = -1;
         int n_fd_passed;
         const char *console = NULL;
-        struct termios saved_attr, raw_attr;
         sigset_t mask;
         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();
         _cleanup_fdset_free_ FDSet *fds = NULL;
 
         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);
-
-        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 (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;
         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;
 
         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) {
 
                 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 ++;
 
                         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;
 
                         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);
                         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;
                         }
 
                                 goto child_fail;
                         }
 
-                        close_pipe(pipefd2);
-
                         r = register_machine();
                         if (r < 0)
                                 goto finish;
                         r = register_machine();
                         if (r < 0)
                                 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);
                 fds = NULL;
 
                 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);
 
                 k = wait_for_terminate(pid, &status);
+                pid = 0;
+
                 if (k < 0) {
                         r = EXIT_FAILURE;
                         break;
                 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) {
                 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;
                         }
 
                                 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) {
                         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) {
                         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) {
 
                         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 {
                         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:
                         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);
 
         if (pid > 0)
                 kill(pid, SIGKILL);
 
index 1e2852b1af1e0c60d9541bc2feada9c4ba6243b0..7225b933ff35a7f3146e13e9a537132311f052f9 100644 (file)
 #include "util.h"
 #include "ptyfwd.h"
 
 #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;
         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);
         _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;
 
                 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)
                 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;
                 }
 
                         return -errno;
                 }
 
-                assert(nfds >= 1);
+                if (nfds == 0)
+                        return 0;
 
                 for (i = 0; i < nfds; i++) {
                         if (ev[i].data.fd == STDIN_FILENO) {
 
                 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;
 
                                 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) ||
                 }
 
                 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) {
 
                                 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;
                                                 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;
                                         }
                                                 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;
                                         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);
                                 if (k < 0) {
 
                                 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;
                                                 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;
                                         }
                                                 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) {
 
                                 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;
                                                 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;
                                         }
                                                 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) {
 
                                 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;
                                                 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;
                                         }
                                                 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;
                                 }
                         }
                                         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;
+
+}
index dc35b4d9801466fb0d992f45f28828d78a36940f..d90b8084769ce77efa7673600e10386fd44a10e4 100644 (file)
@@ -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*, pclose);
 DEFINE_TRIVIAL_CLEANUP_FUNC(DIR*, closedir);
 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_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))
 
 _malloc_  _alloc_(1, 2) static inline void *malloc_multiply(size_t a, size_t b) {
         if (_unlikely_(b == 0 || a > ((size_t) -1) / b))
index 6a67f3f60887aa7759d1a132f9568fb97748cac8..5fd9ccadb119f4b8564f810951efb2a0e14aa417 100644 (file)
@@ -9,6 +9,8 @@
 /systemd-suspend.service
 /console-getty.service
 /console-getty.service.m4
 /systemd-suspend.service
 /console-getty.service
 /console-getty.service.m4
+/container-getty@.service
+/container-getty@.service.m4
 /systemd-journald.service
 /user@.service
 /systemd-logind.service
 /systemd-journald.service
 /user@.service
 /systemd-logind.service
index 2fd9dd8478ababfc8748bace6a4abbcb3f2a6af0..464732c742a642fd0139114c658da363562ce9f1 100644 (file)
@@ -15,7 +15,7 @@ After=rc-local.service
 Before=getty.target
 
 [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
 Type=idle
 Restart=always
 RestartSec=0
diff --git a/units/container-getty@.service.m4.in b/units/container-getty@.service.m4.in
new file mode 100644 (file)
index 0000000..abc58cb
--- /dev/null
@@ -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