13
13
#include <utime.h>
14
14
#ifndef WIN32
15
15
#include <sys/stat.h> /* for stat() */
16
+ #include <sys/time.h> /* for setitimer() */
16
17
#include <fcntl.h> /* open() flags */
17
18
#include <unistd.h> /* for geteuid(), getpid(), stat() */
18
19
#else
@@ -4894,15 +4895,76 @@ do_watch(PQExpBuffer query_buf, double sleep)
4894
4895
const char * strftime_fmt ;
4895
4896
const char * user_title ;
4896
4897
char * title ;
4898
+ const char * pagerprog = NULL ;
4899
+ FILE * pagerpipe = NULL ;
4897
4900
int title_len ;
4898
4901
int res = 0 ;
4902
+ #ifndef WIN32
4903
+ sigset_t sigalrm_sigchld_sigint ;
4904
+ sigset_t sigalrm_sigchld ;
4905
+ sigset_t sigint ;
4906
+ struct itimerval interval ;
4907
+ bool done = false;
4908
+ #endif
4899
4909
4900
4910
if (!query_buf || query_buf -> len <= 0 )
4901
4911
{
4902
4912
pg_log_error ("\\watch cannot be used with an empty query" );
4903
4913
return false;
4904
4914
}
4905
4915
4916
+ #ifndef WIN32
4917
+ sigemptyset (& sigalrm_sigchld_sigint );
4918
+ sigaddset (& sigalrm_sigchld_sigint , SIGCHLD );
4919
+ sigaddset (& sigalrm_sigchld_sigint , SIGALRM );
4920
+ sigaddset (& sigalrm_sigchld_sigint , SIGINT );
4921
+
4922
+ sigemptyset (& sigalrm_sigchld );
4923
+ sigaddset (& sigalrm_sigchld , SIGCHLD );
4924
+ sigaddset (& sigalrm_sigchld , SIGALRM );
4925
+
4926
+ sigemptyset (& sigint );
4927
+ sigaddset (& sigint , SIGINT );
4928
+
4929
+ /*
4930
+ * Block SIGALRM and SIGCHLD before we start the timer and the pager (if
4931
+ * configured), to avoid races. sigwait() will receive them.
4932
+ */
4933
+ sigprocmask (SIG_BLOCK , & sigalrm_sigchld , NULL );
4934
+
4935
+ /*
4936
+ * Set a timer to interrupt sigwait() so we can run the query at the
4937
+ * requested intervals.
4938
+ */
4939
+ interval .it_value .tv_sec = sleep_ms / 1000 ;
4940
+ interval .it_value .tv_usec = (sleep_ms % 1000 ) * 1000 ;
4941
+ interval .it_interval = interval .it_value ;
4942
+ if (setitimer (ITIMER_REAL , & interval , NULL ) < 0 )
4943
+ {
4944
+ pg_log_error ("could not set timer: %m" );
4945
+ done = true;
4946
+ }
4947
+ #endif
4948
+
4949
+ /*
4950
+ * For \watch, we ignore the size of the result and always use the pager
4951
+ * if PSQL_WATCH_PAGER is set. We also ignore the regular PSQL_PAGER or
4952
+ * PAGER environment variables, because traditional pagers probably won't
4953
+ * be very useful for showing a stream of results.
4954
+ */
4955
+ #ifndef WIN32
4956
+ pagerprog = getenv ("PSQL_WATCH_PAGER" );
4957
+ #endif
4958
+ if (pagerprog && myopt .topt .pager )
4959
+ {
4960
+ disable_sigpipe_trap ();
4961
+ pagerpipe = popen (pagerprog , "w" );
4962
+
4963
+ if (!pagerpipe )
4964
+ /* silently proceed without pager */
4965
+ restore_sigpipe_trap ();
4966
+ }
4967
+
4906
4968
/*
4907
4969
* Choose format for timestamps. We might eventually make this a \pset
4908
4970
* option. In the meantime, using a variable for the format suppresses
@@ -4911,10 +4973,12 @@ do_watch(PQExpBuffer query_buf, double sleep)
4911
4973
strftime_fmt = "%c" ;
4912
4974
4913
4975
/*
4914
- * Set up rendering options, in particular, disable the pager, because
4915
- * nobody wants to be prompted while watching the output of 'watch' .
4976
+ * Set up rendering options, in particular, disable the pager unless
4977
+ * PSQL_WATCH_PAGER was successfully launched .
4916
4978
*/
4917
- myopt .topt .pager = 0 ;
4979
+ if (!pagerpipe )
4980
+ myopt .topt .pager = 0 ;
4981
+
4918
4982
4919
4983
/*
4920
4984
* If there's a title in the user configuration, make sure we have room
@@ -4929,7 +4993,6 @@ do_watch(PQExpBuffer query_buf, double sleep)
4929
4993
{
4930
4994
time_t timer ;
4931
4995
char timebuf [128 ];
4932
- long i ;
4933
4996
4934
4997
/*
4935
4998
* Prepare title for output. Note that we intentionally include a
@@ -4948,7 +5011,7 @@ do_watch(PQExpBuffer query_buf, double sleep)
4948
5011
myopt .title = title ;
4949
5012
4950
5013
/* Run the query and print out the results */
4951
- res = PSQLexecWatch (query_buf -> data , & myopt );
5014
+ res = PSQLexecWatch (query_buf -> data , & myopt , pagerpipe );
4952
5015
4953
5016
/*
4954
5017
* PSQLexecWatch handles the case where we can no longer repeat the
@@ -4957,6 +5020,11 @@ do_watch(PQExpBuffer query_buf, double sleep)
4957
5020
if (res <= 0 )
4958
5021
break ;
4959
5022
5023
+ if (pagerpipe && ferror (pagerpipe ))
5024
+ break ;
5025
+
5026
+ #ifdef WIN32
5027
+
4960
5028
/*
4961
5029
* Set up cancellation of 'watch' via SIGINT. We redo this each time
4962
5030
* through the loop since it's conceivable something inside
@@ -4967,12 +5035,10 @@ do_watch(PQExpBuffer query_buf, double sleep)
4967
5035
4968
5036
/*
4969
5037
* Enable 'watch' cancellations and wait a while before running the
4970
- * query again. Break the sleep into short intervals (at most 1s)
4971
- * since pg_usleep isn't interruptible on some platforms.
5038
+ * query again. Break the sleep into short intervals (at most 1s).
4972
5039
*/
4973
5040
sigint_interrupt_enabled = true;
4974
- i = sleep_ms ;
4975
- while (i > 0 )
5041
+ for (long i = sleep_ms ; i > 0 ;)
4976
5042
{
4977
5043
long s = Min (i , 1000L );
4978
5044
@@ -4982,8 +5048,57 @@ do_watch(PQExpBuffer query_buf, double sleep)
4982
5048
i -= s ;
4983
5049
}
4984
5050
sigint_interrupt_enabled = false;
5051
+ #else
5052
+ /* sigwait() will handle SIGINT. */
5053
+ sigprocmask (SIG_BLOCK , & sigint , NULL );
5054
+ if (cancel_pressed )
5055
+ done = true;
5056
+
5057
+ /* Wait for SIGINT, SIGCHLD or SIGALRM. */
5058
+ while (!done )
5059
+ {
5060
+ int signal_received ;
5061
+
5062
+ if (sigwait (& sigalrm_sigchld_sigint , & signal_received ) < 0 )
5063
+ {
5064
+ /* Some other signal arrived? */
5065
+ if (errno == EINTR )
5066
+ continue ;
5067
+ else
5068
+ {
5069
+ pg_log_error ("could not wait for signals: %m" );
5070
+ done = true;
5071
+ break ;
5072
+ }
5073
+ }
5074
+ /* On ^C or pager exit, it's time to stop running the query. */
5075
+ if (signal_received == SIGINT || signal_received == SIGCHLD )
5076
+ done = true;
5077
+ /* Otherwise, we must have SIGALRM. Time to run the query again. */
5078
+ break ;
5079
+ }
5080
+
5081
+ /* Unblock SIGINT so that slow queries can be interrupted. */
5082
+ sigprocmask (SIG_UNBLOCK , & sigint , NULL );
5083
+ if (done )
5084
+ break ;
5085
+ #endif
4985
5086
}
4986
5087
5088
+ if (pagerpipe )
5089
+ {
5090
+ pclose (pagerpipe );
5091
+ restore_sigpipe_trap ();
5092
+ }
5093
+
5094
+ #ifndef WIN32
5095
+ /* Disable the interval timer. */
5096
+ memset (& interval , 0 , sizeof (interval ));
5097
+ setitimer (ITIMER_REAL , & interval , NULL );
5098
+ /* Unblock SIGINT, SIGCHLD and SIGALRM. */
5099
+ sigprocmask (SIG_UNBLOCK , & sigalrm_sigchld_sigint , NULL );
5100
+ #endif
5101
+
4987
5102
pg_free (title );
4988
5103
return (res >= 0 );
4989
5104
}
0 commit comments