Skip to content

Commit

Permalink
When initializing the child for a detached proxy, if the original pro…
Browse files Browse the repository at this point in the history
…cess is the foreground process for its controlling terminal, try to make the new process the foreground process for the terminal.

Resolves #3875
  • Loading branch information
rocallahan committed Nov 30, 2024
1 parent 419c235 commit 65b941c
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 6 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1507,6 +1507,7 @@ set(TESTS_WITH_PROGRAM
crash_in_function
daemon_read
dconf_mock
detach_terminal
dev_tty
x86/diversion_rdtsc
diversion_sigtrap
Expand Down
50 changes: 44 additions & 6 deletions src/RecordCommand.cc
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,30 @@ static void reset_uid_sudo() {
prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0);
}

static ScopedFd open_controlling_terminal_if_foreground_process_group_leader() {
char path[L_ctermid + 1];
ctermid(path);
ScopedFd terminal_fd(path, O_RDONLY);
if (terminal_fd.is_open()) {
pid_t fg_process_group = tcgetpgrp(terminal_fd);
if (fg_process_group != getpid()) {
terminal_fd.close();
}
}
return terminal_fd;
}

static void detach_teleport() {
int ret = syscall(SYS_rrcall_detach_teleport, (uintptr_t)0, (uintptr_t)0,
(uintptr_t)0, (uintptr_t)0, (uintptr_t)0, (uintptr_t)0);
if (ret < 0) {
FATAL() << "Failed to detach from parent rr";
}
if (running_under_rr(false)) {
FATAL() << "Detaching from parent rr did not work";
}
}

int RecordCommand::run(vector<string>& args) {
RecordFlags flags;
while (parse_record_arg(args, flags)) {
Expand All @@ -829,14 +853,28 @@ int RecordCommand::run(vector<string>& args) {
return 1;
case NESTED_DETACH:
case NESTED_RELEASE: {
int ret = syscall(SYS_rrcall_detach_teleport, (uintptr_t)0, (uintptr_t)0,
(uintptr_t)0, (uintptr_t)0, (uintptr_t)0, (uintptr_t)0);
if (ret < 0) {
FATAL() << "Failed to detach from parent rr";
bool is_process_group_leader = getpgrp() == getpid();
ScopedFd terminal_fd = open_controlling_terminal_if_foreground_process_group_leader();

detach_teleport();

if (is_process_group_leader) {
setpgid(0, 0);
}
if (running_under_rr(false)) {
FATAL() << "Detaching from parent rr did not work";
if (terminal_fd.is_open()) {
struct sigaction sa;
struct sigaction sa_old;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SIG_IGN;
// Ignore SIGTTOU while we change settings, we don't want it to stop us
sigaction(SIGTTOU, &sa, &sa_old);
int ret = tcsetpgrp(terminal_fd, getpid());
if (ret) {
LOG(warn) << "Failed to make ourselves the foreground process: " << errno_name(errno);
}
sigaction(SIGTTOU, &sa_old, nullptr);
}

if (flags.nested == NESTED_RELEASE) {
exec_child(args);
return 1;
Expand Down
76 changes: 76 additions & 0 deletions src/test/detach_terminal.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/* -*- Mode: C; tab-width: 8; c-basic-offset: 2; indent-tabs-mode: nil; -*- */

#include "util.h"

int main(int argc, char** argv, char** envp) {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SIG_IGN;
sigaction(SIGTTIN, &sa, NULL);
sigaction(SIGTTOU, &sa, NULL);

if (argc >= 2 && strcmp(argv[1], "--newtty") == 0) {
int parent_to_child[2];
pipe(parent_to_child);
int child_to_parent[2];
pipe(child_to_parent);

// To call setsid() successfully this process must not be a
// process group leader. Create a child process, make it a process
// group leader, and move our process into that process group.
pid_t child = fork();
if (child == 0) {
test_assert(setpgid(0, 0) == 0);
test_assert(write(child_to_parent[1], "x", 1) == 1);
char ch;
test_assert(read(parent_to_child[0], &ch, 1) == 1);
return 0;
}
char ch;
test_assert(read(child_to_parent[0], &ch, 1) == 1);
test_assert(setpgid(getpid(), child) == 0);
test_assert(write(parent_to_child[1], "x", 1) == 1);

int status;
wait(&status);
test_assert(WIFEXITED(status) && WEXITSTATUS(status) == 0);

test_assert(setsid() >= 0);

// Create a new terminal, make it the controlling terminal
// for this process, and exec the rest of the command line.
int control_fd = posix_openpt(O_RDWR);
test_assert(control_fd >= 0);
test_assert(grantpt(control_fd) == 0);
test_assert(unlockpt(control_fd) == 0);

char buf[PATH_MAX];
ptsname_r(control_fd, buf, sizeof(buf));
int term_fd = open(buf, O_RDWR);
test_assert(term_fd >= 0);

test_assert(dup2(term_fd, STDIN_FILENO) == STDIN_FILENO);

execve(argv[2], argv + 2, envp);
return 65;
}

if (argc >= 2 && strcmp(argv[1], "--newpgrp") == 0) {
// Make this process the foreground process for our terminal
// and exec the rest of the command line.
test_assert(setpgrp() >= 0);
test_assert(tcsetpgrp(STDIN_FILENO, getpid()) >= 0);
execve(argv[2], argv + 2, envp);
return 66;
}

// Check that we are the foreground process for our terminal.
int pgrp = tcgetpgrp(STDIN_FILENO);
if (pgrp != getpid()) {
atomic_printf("tcgetpgrp() == %d, expected %d\n", pgrp, getpid());
return 67;
}

atomic_puts("EXIT-SUCCESS");
return 0;
}
7 changes: 7 additions & 0 deletions src/test/detach_terminal.run
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
source `dirname $0`/util.sh
skip_if_test_32_bit
actual_rr=$(which $RR_EXE)
RR_EXE="detach_terminal$bitness --newtty $actual_rr"
just_record detach_terminal$bitness "--newpgrp $actual_rr record --nested=release detach_terminal$bitness"
replay
check_record EXIT-SUCCESS

0 comments on commit 65b941c

Please sign in to comment.