src/bin/psql/command.c
static bool
do_watch(PQExpBuffer query_buf, double sleep)
{
	long		sleep_ms = (long) (sleep * 1000);
	printQueryOpt myopt = pset.popt;
	const char *strftime_fmt;
	const char *user_title;
	char	   *title;
	const char *pagerprog = NULL;
	FILE	   *pagerpipe = NULL;
	int			title_len;
	int			res = 0;
#ifdef HAVE_POSIX_DECL_SIGWAIT
	sigset_t	sigalrm_sigchld_sigint;
	sigset_t	sigalrm_sigchld;
	sigset_t	sigint;
	struct itimerval interval;
	bool		done = false;
#endif

	if (!query_buf || query_buf->len <= 0)
	{
		pg_log_error("\\watch cannot be used with an empty query");
		return false;
	}

#ifdef HAVE_POSIX_DECL_SIGWAIT
	sigemptyset(&sigalrm_sigchld_sigint);
	sigaddset(&sigalrm_sigchld_sigint, SIGCHLD);
	sigaddset(&sigalrm_sigchld_sigint, SIGALRM);
	sigaddset(&sigalrm_sigchld_sigint, SIGINT);

	sigemptyset(&sigalrm_sigchld);
	sigaddset(&sigalrm_sigchld, SIGCHLD);
	sigaddset(&sigalrm_sigchld, SIGALRM);

	sigemptyset(&sigint);
	sigaddset(&sigint, SIGINT);

	/*
	 * Block SIGALRM and SIGCHLD before we start the timer and the pager (if
	 * configured), to avoid races.  sigwait() will receive them.
	 */
	sigprocmask(SIG_BLOCK, &sigalrm_sigchld, NULL);

	/*
	 * Set a timer to interrupt sigwait() so we can run the query at the
	 * requested intervals.
	 */
	interval.it_value.tv_sec = sleep_ms / 1000;
	interval.it_value.tv_usec = (sleep_ms % 1000) * 1000;
	interval.it_interval = interval.it_value;
	if (setitimer(ITIMER_REAL, &interval, NULL) < 0)
	{
		pg_log_error("could not set timer: %m");
		done = true;
	}
#endif

	/*
	 * For \watch, we ignore the size of the result and always use the pager
	 * if PSQL_WATCH_PAGER is set.  We also ignore the regular PSQL_PAGER or
	 * PAGER environment variables, because traditional pagers probably won't
	 * be very useful for showing a stream of results.
	 */
#ifdef HAVE_POSIX_DECL_SIGWAIT
	pagerprog = getenv("PSQL_WATCH_PAGER");
#endif
	if (pagerprog && myopt.topt.pager)
	{
		disable_sigpipe_trap();
		pagerpipe = popen(pagerprog, "w");

		if (!pagerpipe)
			/* silently proceed without pager */
			restore_sigpipe_trap();
	}

	/*
	 * Choose format for timestamps.  We might eventually make this a \pset
	 * option.  In the meantime, using a variable for the format suppresses
	 * overly-anal-retentive gcc warnings about %c being Y2K sensitive.
	 */
	strftime_fmt = "%c";

	/*
	 * Set up rendering options, in particular, disable the pager unless
	 * PSQL_WATCH_PAGER was successfully launched.
	 */
	if (!pagerpipe)
		myopt.topt.pager = 0;


	/*
	 * If there's a title in the user configuration, make sure we have room
	 * for it in the title buffer.  Allow 128 bytes for the timestamp plus 128
	 * bytes for the rest.
	 */
	user_title = myopt.title;
	title_len = (user_title ? strlen(user_title) : 0) + 256;
	title = pg_malloc(title_len);

	for (;;)
	{
		time_t		timer;
		char		timebuf[128];

		/*
		 * Prepare title for output.  Note that we intentionally include a
		 * newline at the end of the title; this is somewhat historical but it
		 * makes for reasonably nicely formatted output in simple cases.
		 */
		timer = time(NULL);
		strftime(timebuf, sizeof(timebuf), strftime_fmt, localtime(&timer));

		if (user_title)
			snprintf(title, title_len, _("%s\t%s (every %gs)\n"),
					 user_title, timebuf, sleep);
		else
			snprintf(title, title_len, _("%s (every %gs)\n"),
					 timebuf, sleep);
		myopt.title = title;

		/* Run the query and print out the results */
		res = PSQLexecWatch(query_buf->data, &myopt, pagerpipe);

		/*
		 * PSQLexecWatch handles the case where we can no longer repeat the
		 * query, and returns 0 or -1.
		 */
		if (res <= 0)
			break;

		if (pagerpipe && ferror(pagerpipe))
			break;

#ifndef HAVE_POSIX_DECL_SIGWAIT

		/*
		 * Set up cancellation of 'watch' via SIGINT.  We redo this each time
		 * through the loop since it's conceivable something inside
		 * PSQLexecWatch could change sigint_interrupt_jmp.
		 */
		if (sigsetjmp(sigint_interrupt_jmp, 1) != 0)
			break;

		/*
		 * Enable 'watch' cancellations and wait a while before running the
		 * query again.  Break the sleep into short intervals (at most 1s).
		 */
		sigint_interrupt_enabled = true;
		for (long i = sleep_ms; i > 0;)
		{
			long		s = Min(i, 1000L);

			pg_usleep(s * 1000L);
			if (cancel_pressed)
				break;
			i -= s;
		}
		sigint_interrupt_enabled = false;
#else
		/* sigwait() will handle SIGINT. */
		sigprocmask(SIG_BLOCK, &sigint, NULL);
		if (cancel_pressed)
			done = true;

		/* Wait for SIGINT, SIGCHLD or SIGALRM. */
		while (!done)
		{
			int			signal_received;

			errno = sigwait(&sigalrm_sigchld_sigint, &signal_received);
			if (errno != 0)
			{
				/* Some other signal arrived? */
				if (errno == EINTR)
					continue;
				else
				{
					pg_log_error("could not wait for signals: %m");
					done = true;
					break;
				}
			}
			/* On ^C or pager exit, it's time to stop running the query. */
			if (signal_received == SIGINT || signal_received == SIGCHLD)
				done = true;
			/* Otherwise, we must have SIGALRM.  Time to run the query again. */
			break;
		}

		/* Unblock SIGINT so that slow queries can be interrupted. */
		sigprocmask(SIG_UNBLOCK, &sigint, NULL);
		if (done)
			break;
#endif
	}

	if (pagerpipe)
	{
		pclose(pagerpipe);
		restore_sigpipe_trap();
	}

#ifdef HAVE_POSIX_DECL_SIGWAIT
	/* Disable the interval timer. */
	memset(&interval, 0, sizeof(interval));
	setitimer(ITIMER_REAL, &interval, NULL);
	/* Unblock SIGINT, SIGCHLD and SIGALRM. */
	sigprocmask(SIG_UNBLOCK, &sigalrm_sigchld_sigint, NULL);
#endif

	pg_free(title);
	return (res >= 0);
}
/*
 * do_watch -- handler for \watch
 *
 * We break this out of exec_command to avoid having to plaster "volatile"
 * onto a bunch of exec_command's variables to silence stupider compilers.
 */
- handler for \watch
We break this out of exec_command to avoid having to plaster "volatile"
onto a bunch of exec_command's variables to silence stupider compilers.
- handler for \ watch We break this out of exec_command to avoid having to plaster " volatile " onto a bunch of exec_command ' s variables to silence stupider compilers .
static bool
do_watch(PQExpBuffer query_buf, double sleep)
{
	long		sleep_ms = (long) (sleep * 1000);
	printQueryOpt myopt = pset.popt;
	const char *strftime_fmt;
	const char *user_title;
	char	   *title;
	const char *pagerprog = NULL;
	FILE	   *pagerpipe = NULL;
	int			title_len;
	int			res = 0;
#ifdef HAVE_POSIX_DECL_SIGWAIT
	sigset_t	sigalrm_sigchld_sigint;
	sigset_t	sigalrm_sigchld;
	sigset_t	sigint;
	struct itimerval interval;
	bool		done = false;
#endif
	if (!query_buf || query_buf->len <= 0)
	{
		pg_log_error("\\watch cannot be used with an empty query");
		return false;
	}
#ifdef HAVE_POSIX_DECL_SIGWAIT
	sigemptyset(&sigalrm_sigchld_sigint);
	sigaddset(&sigalrm_sigchld_sigint, SIGCHLD);
	sigaddset(&sigalrm_sigchld_sigint, SIGALRM);
	sigaddset(&sigalrm_sigchld_sigint, SIGINT);
	sigemptyset(&sigalrm_sigchld);
	sigaddset(&sigalrm_sigchld, SIGCHLD);
	sigaddset(&sigalrm_sigchld, SIGALRM);
	sigemptyset(&sigint);
	sigaddset(&sigint, SIGINT);
	sigprocmask(SIG_BLOCK, &sigalrm_sigchld, NULL);
	interval.it_value.tv_sec = sleep_ms / 1000;
	interval.it_value.tv_usec = (sleep_ms % 1000) * 1000;
	interval.it_interval = interval.it_value;
	if (setitimer(ITIMER_REAL, &interval, NULL) < 0)
	{
		pg_log_error("could not set timer: %m");
		done = true;
	}
#endif
#ifdef HAVE_POSIX_DECL_SIGWAIT
	pagerprog = getenv("PSQL_WATCH_PAGER");
#endif
	if (pagerprog && myopt.topt.pager)
	{
		disable_sigpipe_trap();
		pagerpipe = popen(pagerprog, "w");
		if (!pagerpipe)
			restore_sigpipe_trap();
	}
	strftime_fmt = "%c";
	if (!pagerpipe)
		myopt.topt.pager = 0;
	user_title = myopt.title;
	title_len = (user_title ? strlen(user_title) : 0) + 256;
	title = pg_malloc(title_len);
	for (;;)
	{
		time_t		timer;
		char		timebuf[128];
		timer = time(NULL);
		strftime(timebuf, sizeof(timebuf), strftime_fmt, localtime(&timer));
		if (user_title)
			snprintf(title, title_len, _("%s\t%s (every %gs)\n"),
					 user_title, timebuf, sleep);
		else
			snprintf(title, title_len, _("%s (every %gs)\n"),
					 timebuf, sleep);
		myopt.title = title;
		res = PSQLexecWatch(query_buf->data, &myopt, pagerpipe);
		if (res <= 0)
			break;
		if (pagerpipe && ferror(pagerpipe))
			break;
#ifndef HAVE_POSIX_DECL_SIGWAIT
		if (sigsetjmp(sigint_interrupt_jmp, 1) != 0)
			break;
		sigint_interrupt_enabled = true;
		for (long i = sleep_ms; i > 0;)
		{
			long		s = Min(i, 1000L);
			pg_usleep(s * 1000L);
			if (cancel_pressed)
				break;
			i -= s;
		}
		sigint_interrupt_enabled = false;
#else
		sigprocmask(SIG_BLOCK, &sigint, NULL);
		if (cancel_pressed)
			done = true;
		while (!done)
		{
			int			signal_received;
			errno = sigwait(&sigalrm_sigchld_sigint, &signal_received);
			if (errno != 0)
			{
				if (errno == EINTR)
					continue;
				else
				{
					pg_log_error("could not wait for signals: %m");
					done = true;
					break;
				}
			}
			if (signal_received == SIGINT || signal_received == SIGCHLD)
				done = true;
			break;
		}
		sigprocmask(SIG_UNBLOCK, &sigint, NULL);
		if (done)
			break;
#endif
	}
	if (pagerpipe)
	{
		pclose(pagerpipe);
		restore_sigpipe_trap();
	}
#ifdef HAVE_POSIX_DECL_SIGWAIT
	memset(&interval, 0, sizeof(interval));
	setitimer(ITIMER_REAL, &interval, NULL);
	sigprocmask(SIG_UNBLOCK, &sigalrm_sigchld_sigint, NULL);
#endif
	pg_free(title);
	return (res >= 0);
}
static bool do_watch ( PQExpBuffer query_buf , double sleep ) { long sleep_ms = ( long ) ( sleep * 1000 ) ; printQueryOpt myopt = pset . popt ; const char * strftime_fmt ; const char * user_title ; char * title ; const char * pagerprog = NULL ; FILE * pagerpipe = NULL ; int title_len ; int res = 0 ; #ifdef HAVE_POSIX_DECL_SIGWAIT sigset_t sigalrm_sigchld_sigint ; sigset_t sigalrm_sigchld ; sigset_t sigint ; struct itimerval interval ; bool done = false ; #endif if ( ! query_buf || query_buf -> len <= 0 ) { pg_log_error ( " \\ " ) ; return false ; } #ifdef HAVE_POSIX_DECL_SIGWAIT sigemptyset ( & sigalrm_sigchld_sigint ) ; sigaddset ( & sigalrm_sigchld_sigint , SIGCHLD ) ; sigaddset ( & sigalrm_sigchld_sigint , SIGALRM ) ; sigaddset ( & sigalrm_sigchld_sigint , SIGINT ) ; sigemptyset ( & sigalrm_sigchld ) ; sigaddset ( & sigalrm_sigchld , SIGCHLD ) ; sigaddset ( & sigalrm_sigchld , SIGALRM ) ; sigemptyset ( & sigint ) ; sigaddset ( & sigint , SIGINT ) ; sigprocmask ( SIG_BLOCK , & sigalrm_sigchld , NULL ) ; interval . it_value . tv_sec = sleep_ms / 1000 ; interval . it_value . tv_usec = ( sleep_ms % 1000 ) * 1000 ; interval . it_interval = interval . it_value ; if ( setitimer ( ITIMER_REAL , & interval , NULL ) < 0 ) { pg_log_error ( " " ) ; done = true ; } #endif #ifdef HAVE_POSIX_DECL_SIGWAIT pagerprog = getenv ( " " ) ; #endif if ( pagerprog && myopt . topt . pager ) { disable_sigpipe_trap ( ) ; pagerpipe = popen ( pagerprog , " " ) ; if ( ! pagerpipe ) restore_sigpipe_trap ( ) ; } strftime_fmt = " " ; if ( ! pagerpipe ) myopt . topt . pager = 0 ; user_title = myopt . title ; title_len = ( user_title ? strlen ( user_title ) : 0 ) + 256 ; title = pg_malloc ( title_len ) ; for ( ; ; ) { time_t timer ; char timebuf [ 128 ] ; timer = time ( NULL ) ; strftime ( timebuf , sizeof ( timebuf ) , strftime_fmt , localtime ( & timer ) ) ; if ( user_title ) snprintf ( title , title_len , _ ( " \t \n " ) , user_title , timebuf , sleep ) ; else snprintf ( title , title_len , _ ( " \n " ) , timebuf , sleep ) ; myopt . title = title ; res = PSQLexecWatch ( query_buf -> data , & myopt , pagerpipe ) ; if ( res <= 0 ) break ; if ( pagerpipe && ferror ( pagerpipe ) ) break ; #ifndef HAVE_POSIX_DECL_SIGWAIT if ( sigsetjmp ( sigint_interrupt_jmp , 1 ) != 0 ) break ; sigint_interrupt_enabled = true ; for ( long i = sleep_ms ; i > 0 ; ) { long s = Min ( i , 1000L ) ; pg_usleep ( s * 1000L ) ; if ( cancel_pressed ) break ; i -= s ; } sigint_interrupt_enabled = false ; #else sigprocmask ( SIG_BLOCK , & sigint , NULL ) ; if ( cancel_pressed ) done = true ; while ( ! done ) { int signal_received ; errno = sigwait ( & sigalrm_sigchld_sigint , & signal_received ) ; if ( errno != 0 ) { if ( errno == EINTR ) continue ; else { pg_log_error ( " " ) ; done = true ; break ; } } if ( signal_received == SIGINT || signal_received == SIGCHLD ) done = true ; break ; } sigprocmask ( SIG_UNBLOCK , & sigint , NULL ) ; if ( done ) break ; #endif } if ( pagerpipe ) { pclose ( pagerpipe ) ; restore_sigpipe_trap ( ) ; } #ifdef HAVE_POSIX_DECL_SIGWAIT memset ( & interval , 0 , sizeof ( interval ) ) ; setitimer ( ITIMER_REAL , & interval , NULL ) ; sigprocmask ( SIG_UNBLOCK , & sigalrm_sigchld_sigint , NULL ) ; #endif pg_free ( title ) ; return ( res >= 0 ) ; }
do_watch -- handler for \watch
 We break this out of exec_command to avoid having to plaster "volatile"
 onto a bunch of exec_command's variables to silence stupider compilers.
do_watch -- handler for \ watch We break this out of exec_command to avoid having to plaster " volatile " onto a bunch of exec_command ' s variables to silence stupider compilers .
/*
	 * Block SIGALRM and SIGCHLD before we start the timer and the pager (if
	 * configured), to avoid races.  sigwait() will receive them.
	 */ /*
	 * Set a timer to interrupt sigwait() so we can run the query at the
	 * requested intervals.
	 */ /*
	 * For \watch, we ignore the size of the result and always use the pager
	 * if PSQL_WATCH_PAGER is set.  We also ignore the regular PSQL_PAGER or
	 * PAGER environment variables, because traditional pagers probably won't
	 * be very useful for showing a stream of results.
	 */ /* silently proceed without pager */ /*
	 * Choose format for timestamps.  We might eventually make this a \pset
	 * option.  In the meantime, using a variable for the format suppresses
	 * overly-anal-retentive gcc warnings about %c being Y2K sensitive.
	 */ /*
	 * Set up rendering options, in particular, disable the pager unless
	 * PSQL_WATCH_PAGER was successfully launched.
	 */ /*
	 * If there's a title in the user configuration, make sure we have room
	 * for it in the title buffer.  Allow 128 bytes for the timestamp plus 128
	 * bytes for the rest.
	 */ /*
		 * Prepare title for output.  Note that we intentionally include a
		 * newline at the end of the title; this is somewhat historical but it
		 * makes for reasonably nicely formatted output in simple cases.
		 */ /* Run the query and print out the results */ /*
		 * PSQLexecWatch handles the case where we can no longer repeat the
		 * query, and returns 0 or -1.
		 */ /*
		 * Set up cancellation of 'watch' via SIGINT.  We redo this each time
		 * through the loop since it's conceivable something inside
		 * PSQLexecWatch could change sigint_interrupt_jmp.
		 */ /*
		 * Enable 'watch' cancellations and wait a while before running the
		 * query again.  Break the sleep into short intervals (at most 1s).
		 */ /* sigwait() will handle SIGINT. */ /* Wait for SIGINT, SIGCHLD or SIGALRM. */ /* Some other signal arrived? */ /* On ^C or pager exit, it's time to stop running the query. */ /* Otherwise, we must have SIGALRM.  Time to run the query again. */ /* Unblock SIGINT so that slow queries can be interrupted. */ /* Disable the interval timer. */ /* Unblock SIGINT, SIGCHLD and SIGALRM. */