Skip to content

Commit

Permalink
Merge pull request #122 from chriskuehl/fix-job-control
Browse files Browse the repository at this point in the history
Hand controlling TTY to child, if we have one
  • Loading branch information
chriskuehl committed Oct 10, 2016
2 parents 1bb4ba1 + e4e2dd0 commit b66cbb0
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 14 deletions.
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

0 comments on commit b66cbb0

Please sign in to comment.