Skip to content

Commit cd575b8

Browse files
committed
shell: pty: prevent eof before all pty data is captured
Problem: When the pty.capture shell option is used (either implicitly or explicitly), an eof for stdout may be written to the output eventlog before all pty data is read. This is particularly true when used with pty.interactive, since in this mode the normal stdout file descriptor for the task is closed immediately when the task is attached to the pty. When capturing pty output for the output eventlog, take a reference on the task's stdout channel so that it doesn't close until the pty has completed reading its data.
1 parent c9dfa49 commit cd575b8

File tree

1 file changed

+46
-3
lines changed

1 file changed

+46
-3
lines changed

src/shell/pty.c

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,15 @@ static void pty_monitor (struct flux_pty *pty, void *data, int len)
7171
flux_plugin_arg_t *args;
7272
int rank;
7373

74-
/* Don't bother sending 0 length output */
75-
if (len == 0)
74+
/* len == 0 indicates pty is closed. If there's a reference on
75+
* stdout, release it here
76+
*/
77+
if (len == 0) {
78+
flux_subprocess_t *p;
79+
if ((p = flux_pty_aux_get (pty, "subprocess")))
80+
flux_subprocess_channel_decref (p, "stdout");
7681
return;
82+
}
7783

7884
rank = ptr2int (flux_pty_aux_get (pty, "rank"));
7985
if (!(args = flux_plugin_arg_create ())
@@ -351,7 +357,11 @@ static int pty_init (flux_plugin_t *p,
351357
*/
352358
if (capture || rank != 0) {
353359
if (flux_pty_aux_set (pty, "shell", shell, NULL) < 0
354-
|| flux_pty_aux_set (pty, "rank", int2ptr (rank), NULL) < 0) {
360+
|| flux_pty_aux_set (pty, "rank", int2ptr (rank), NULL) < 0
361+
|| flux_pty_aux_set (pty,
362+
"capture",
363+
int2ptr (capture),
364+
NULL) < 0) {
355365
shell_log_errno ("flux_pty_aux_set");
356366
goto error;
357367
}
@@ -408,6 +418,38 @@ static int pty_task_exec (flux_plugin_t *p,
408418
return (0);
409419
}
410420

421+
static int pty_task_fork (flux_plugin_t *p,
422+
const char *topic,
423+
flux_plugin_arg_t *args,
424+
void *arg)
425+
{
426+
flux_shell_t *shell = flux_plugin_get_shell (p);
427+
flux_shell_task_t *task;
428+
struct flux_pty *pty;
429+
int rank;
430+
431+
if (!shell)
432+
return shell_log_errno ("failed to get shell object");
433+
434+
if (flux_shell_getopt (shell, "pty", NULL) != 1)
435+
return 0;
436+
437+
if (!(task = flux_shell_current_task (shell))
438+
|| flux_shell_task_info_unpack (task, "{s:i}", "rank", &rank) < 0)
439+
return shell_log_errno ("unable to get task rank");
440+
441+
/* If pty is in capture mode, then take a reference on subprocess
442+
* stdout so that EOF is not read until pty exits.
443+
*/
444+
if ((pty = pty_lookup (shell, rank))
445+
&& ptr2int (flux_pty_aux_get (pty, "capture"))) {
446+
flux_subprocess_t *sp = flux_shell_task_subprocess (task);
447+
flux_subprocess_channel_incref (sp, "stdout");
448+
flux_pty_aux_set (pty, "subprocess", sp, NULL);
449+
}
450+
return (0);
451+
}
452+
411453
static int pty_task_exit (flux_plugin_t *p,
412454
const char *topic,
413455
flux_plugin_arg_t *args,
@@ -446,6 +488,7 @@ struct shell_builtin builtin_pty = {
446488
.name = FLUX_SHELL_PLUGIN_NAME,
447489
.init = pty_init,
448490
.task_exec = pty_task_exec,
491+
.task_fork = pty_task_fork,
449492
.task_exit = pty_task_exit,
450493
};
451494

0 commit comments

Comments
 (0)