Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hand controlling TTY to child, if we have one #122

Merged
merged 2 commits into from
Oct 10, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions dumb-init.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
Expand Down Expand Up @@ -250,6 +251,15 @@ int main(int argc, char *argv[]) {
for (i = 1; i <= MAXSIG; i++)
signal(i, dummy);

/* detach dumb-init from controlling tty */
if (use_setsid && ioctl(STDIN_FILENO, TIOCNOTTY) == -1) {
DEBUG(
"Unable to detach from controlling tty (errno=%d %s).\n",
errno,
strerror(errno)
);
}

child_pid = fork();
if (child_pid < 0) {
PRINTERR("Unable to fork. Exiting.\n");
Expand All @@ -266,6 +276,14 @@ int main(int argc, char *argv[]) {
);
exit(1);
}

if (ioctl(STDIN_FILENO, TIOCSCTTY, 0) == -1) {
DEBUG(
"Unable to attach to controlling tty (errno=%d %s).\n",
errno,
strerror(errno)
);
}
DEBUG("setsid complete.\n");
}
execvp(cmd[0], &cmd[0]);
Expand Down
53 changes: 39 additions & 14 deletions tests/tty_test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
import pty
import re
import termios

import pytest
Expand Down Expand Up @@ -35,24 +36,48 @@ def readall(fd):
result += chunk


def _test(fd):
"""write to tac via the pty and verify its output"""
ttyflags(fd)
assert os.write(fd, b'1\n2\n3\n') == 6
assert os.write(fd, EOF * 2) == 2
output = readall(fd)
assert output == b'3\n2\n1\n', repr(output)
print('PASS')


# disable debug output so it doesn't break our assertion
@pytest.mark.usefixtures('debug_disabled')
def test_tty():
"""
Ensure processes wrapped by dumb-init can write successfully, given a tty
"""
"""Ensure processes under dumb-init can write successfully, given a tty."""
pid, fd = pty.fork()
if pid == 0:
os.execvp('dumb-init', ('dumb-init', 'tac'))
else:
_test(fd)
# write to tac via the pty and verify its output
ttyflags(fd)
assert os.write(fd, b'1\n2\n3\n') == 6
assert os.write(fd, EOF * 2) == 2
output = readall(fd)
assert os.waitpid(pid, 0) == (pid, 0)

assert output == b'3\n2\n1\n', repr(output)


@pytest.mark.usefixtures('both_debug_modes')
@pytest.mark.usefixtures('both_setsid_modes')
def test_child_gets_controlling_tty_if_we_had_one():
"""If dumb-init has a controlling TTY, it should give it to the child.

To test this, we make a new TTY then exec "dumb-init bash" and ensure that
the shell has working job control.
"""
pid, sfd = pty.fork()
if pid == 0:
os.execvpe('./dumb-init', ('dumb-init', 'bash', '-m'), {})
else:
ttyflags(sfd)

# We might get lots of extra output from the shell, so print something
# we can match on easily.
assert os.write(sfd, b'echo "flags are: [[$-]]"\n') == 25
assert os.write(sfd, b'exit 0\n') == 7
output = readall(sfd)
assert os.waitpid(pid, 0) == (pid, 0), output

m = re.search(b'flags are: \[\[([a-zA-Z]+)\]\]\n', output)
assert m, output

# "m" is job control
flags = m.group(1)
assert b'm' in flags