Subject: Re: [PATCH v4 3/6] pager: introduce wait_for_pager

From: Junio C Hamano <[email-masked]>
Date: Tue, 04 Jun 2024 09:25:34 -0700

Rubén Justo <[email-masked]> writes:

>  static struct child_process pager_process;
>  static const char *pager_program;
> -static int close_fd2;
> +static int old_fd1 = -1, old_fd2 = -1;

;-)

Presumably when old_fd2 does not have a valid value (i.e. -1) it
means we did not do dup2() to save it away, and we refrain from
closing #2 in that case?

It is curious that #1 did not have similar problem 2/6 addressed,
as we never redirected it with dup() to save it away.  But now we
do for some reason that the proposed log message did not explain.
We should say something like

    We need to take back the standard output and the standard error
    stream after we are done with an invocation of the pager.  For
    that, save away the original file descriptors 1 and 2 when
    spawning the pager, and restore them once the pager exits.  The
    presence of saved fd#2 can be used to replace the "close_fd2"
    flag introduced in the previous patch.

perhaps.

>  /* Is the value coming back from term_columns() just a guess? */
>  static int term_columns_guessed;
> @@ -24,20 +24,41 @@ static void close_pager_fds(void)
>  {
>  	/* signal EOF to pager */
>  	close(1);
> -	if (close_fd2)
> +	if (old_fd2 != -1)
>  		close(2);
>  }

OK.

>  static void wait_for_pager_atexit(void)
>  {
> +	if (old_fd1 == -1)
> +		return;
> +
>  	fflush(stdout);
>  	fflush(stderr);
>  	close_pager_fds();
>  	finish_command(&pager_process);
>  }
>  
> +void wait_for_pager(void)
> +{
> +	if (old_fd1 == -1)
> +		return;
> +
> +	wait_for_pager_atexit();
> +	unsetenv("GIT_PAGER_IN_USE");
> +	dup2(old_fd1, 1);
> +	old_fd1 = -1;
> +	if (old_fd2 != -1) {
> +		dup2(old_fd2, 2);
> +		old_fd2 = -1;
> +	}
> +}

Presumably these use old_fd1's validity as a signal to see if have
pager running that need to be cleaned up?  It feels a bit unnatural
why we do not ask about such a process the structure that is set up
to control it, namely, the pager_process structure, but this is OK
for now.

It is just a naming issue, but it smells strange that the normal
code path (wait_for_pager()) calls a function that is an atexit
handler, which is more specific and only to be called atexit.

I would have made

	static void finish_pager(void)
	{
		fflush(stdout);
		fflush(stderr);
		close_pager_fds();
		finish_command(&pager_process);
	}

and then called it from the atexit handler and wait_for_pager().

> @@ -113,6 +134,7 @@ void prepare_pager_args(struct child_process *pager_process, const char *pager)
>  
>  void setup_pager(void)
>  {
> +	static int once = 0;
>  	const char *pager = git_pager(isatty(1));
>  
>  	if (!pager)
> @@ -142,16 +164,18 @@ void setup_pager(void)
>  		return;
>  
>  	/* original process continues, but writes to the pipe */
> +	old_fd1 = dup(1);
>  	dup2(pager_process.in, 1);
>  	if (isatty(2)) {
> -		close_fd2 = 1;
> +		old_fd2 = dup(2);
>  		dup2(pager_process.in, 2);
>  	}
>  	close(pager_process.in);

Can dup(2) fail and return -1?

>  
> -	/* this makes sure that the parent terminates after the pager */
> -	sigchain_push_common(wait_for_pager_signal);
> -	atexit(wait_for_pager_atexit);
> +	if (!once++) {
> +		sigchain_push_common(wait_for_pager_signal);
> +		atexit(wait_for_pager_atexit);
> +	}
>  }

Can we give a name better than "once" to this thing?  

>  int pager_in_use(void)
> diff --git a/pager.h b/pager.h
> index b77433026d..103ecac476 100644
> --- a/pager.h
> +++ b/pager.h
> @@ -5,6 +5,7 @@ struct child_process;
>  
>  const char *git_pager(int stdout_is_tty);
>  void setup_pager(void);
> +void wait_for_pager(void);
>  int pager_in_use(void);
>  int term_columns(void);
>  void term_clear_line(void);

Other than that, overall it looks good.

Thanks, will queue.