qrexec: fix stdout flush on process exit

In case a child of qrexec_daemon has exited and there is still data in its
stdout pipe, we need to flush it to the peer. Previously, the case when the
peer is blocked was not handled; it is now. The bug impact was premature EOF.
This commit is contained in:
Rafal Wojtczuk 2011-05-23 15:35:40 +02:00
parent c677f1cc4e
commit e7d2eefecd

View File

@ -51,6 +51,8 @@ struct _client_info {
int stdout_fd;
int stderr_fd;
int exit_status;
int is_exited;
int pid;
int is_blocked;
int is_close_after_flush_needed;
@ -169,6 +171,8 @@ void handle_exec(int client_id, int len)
client_info[client_id].stdin_fd = stdin_fd;
client_info[client_id].stdout_fd = stdout_fd;
client_info[client_id].stderr_fd = stderr_fd;
client_info[client_id].exit_status = 0;
client_info[client_id].is_exited = 0;
client_info[client_id].pid = pid;
client_info[client_id].is_blocked = 0;
client_info[client_id].is_close_after_flush_needed = 0;
@ -233,6 +237,18 @@ void remove_process(int client_id, int status)
update_max_process_fd();
}
// remove process not immediately after it has exited, but after its stdout and stderr has been drained
// previous method implemented in flush_out_err was broken - it cannot work when peer signalled it is blocked
void possibly_remove_process(int client_id)
{
if (client_info[client_id].stdout_fd == -1 &&
client_info[client_id].stderr_fd == -1 &&
client_info[client_id].is_exited)
remove_process(client_id,
client_info[client_id].exit_status);
}
void handle_input(int client_id, int len)
{
char buf[len];
@ -243,8 +259,8 @@ void handle_input(int client_id, int len)
if (len == 0) {
if (client_info[client_id].is_blocked)
client_info[client_id].
is_close_after_flush_needed = 1;
client_info[client_id].is_close_after_flush_needed
= 1;
else {
close(client_info[client_id].stdin_fd);
client_info[client_id].stdin_fd = -1;
@ -339,11 +355,18 @@ void handle_process_data(int fd)
write_all_vchan_ext(buf, ret);
}
if (ret == 0) {
int client_id = process_fd[fd].client_id;
if (process_fd[fd].type == FDTYPE_STDOUT)
client_info[client_id].stdout_fd = -1;
else
client_info[client_id].stderr_fd = -1;
process_fd[fd].type = FDTYPE_INVALID;
process_fd[fd].client_id = -1;
process_fd[fd].is_blocked = 0;
close(fd);
update_max_process_fd();
possibly_remove_process(client_id);
}
if (ret < 0)
remove_process(process_fd[fd].client_id, 127);
@ -376,39 +399,6 @@ void handle_process_data_all(fd_set * select_fds)
handle_process_data(i);
}
void flush_out_err(int client_id)
{
fd_set select_set;
int fd_max = -1;
int i;
int ret;
struct timeval tv;
for (;;) {
FD_ZERO(&select_set);
for (i = 0; i <= max_process_fd; i++) {
if (process_fd[i].type != FDTYPE_INVALID
&& !process_fd[i].is_blocked
&& process_fd[i].client_id == client_id) {
FD_SET(i, &select_set);
fd_max = i;
}
}
if (fd_max == -1)
return;
tv.tv_sec = 0;
tv.tv_usec = 0;
ret = select(fd_max + 1, &select_set, NULL, NULL, &tv);
if (ret < 0 && errno != EINTR) {
perror("select");
exit(1);
}
if (!ret)
return;
handle_process_data_all(&select_set);
}
}
void reap_children()
{
int status;
@ -418,8 +408,9 @@ void reap_children()
client_id = find_info(pid);
if (client_id < 0)
continue;
flush_out_err(client_id);
remove_process(client_id, status);
client_info[client_id].is_exited = 1;
client_info[client_id].exit_status = status;
possibly_remove_process(client_id);
}
child_exited = 0;
}