Line data Source code
1 : /*
2 : Mosh: the mobile shell
3 : Copyright 2012 Keith Winstein
4 :
5 : This program is free software: you can redistribute it and/or modify
6 : it under the terms of the GNU General Public License as published by
7 : the Free Software Foundation, either version 3 of the License, or
8 : (at your option) any later version.
9 :
10 : This program is distributed in the hope that it will be useful,
11 : but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : GNU General Public License for more details.
14 :
15 : You should have received a copy of the GNU General Public License
16 : along with this program. If not, see <http://www.gnu.org/licenses/>.
17 :
18 : In addition, as a special exception, the copyright holders give
19 : permission to link the code of portions of this program with the
20 : OpenSSL library under certain conditions as described in each
21 : individual source file, and distribute linked combinations including
22 : the two.
23 :
24 : You must obey the GNU General Public License in all respects for all
25 : of the code used other than OpenSSL. If you modify file(s) with this
26 : exception, you may extend this exception to your version of the
27 : file(s), but you are not obligated to do so. If you do not wish to do
28 : so, delete this exception statement from your version. If you delete
29 : this exception statement from all source files in the program, then
30 : also delete it here.
31 : */
32 :
33 : #ifndef SELECT_HPP
34 : #define SELECT_HPP
35 :
36 : #include <string.h>
37 : #include <errno.h>
38 : #include <signal.h>
39 : #include <sys/select.h>
40 : #include <assert.h>
41 :
42 : #include "fatal_assert.h"
43 : #include "timestamp.h"
44 :
45 : /* Convenience wrapper for pselect(2).
46 :
47 : Any signals blocked by calling sigprocmask() outside this code will still be
48 : received during Select::select(). So don't do that. */
49 :
50 : class Select {
51 : public:
52 1394 : static Select &get_instance( void ) {
53 : /* COFU may or may not be thread-safe, depending on compiler */
54 1394 : static Select instance;
55 1394 : return instance;
56 : }
57 :
58 : private:
59 900 : Select()
60 900 : : max_fd( -1 )
61 : /* These initializations are not used; they are just
62 : here to appease -Weffc++. */
63 900 : , all_fds( dummy_fd_set )
64 900 : , read_fds( dummy_fd_set )
65 900 : , empty_sigset( dummy_sigset )
66 900 : , consecutive_polls( 0 )
67 : {
68 15300 : FD_ZERO( &all_fds );
69 15300 : FD_ZERO( &read_fds );
70 :
71 1800 : clear_got_signal();
72 900 : fatal_assert( 0 == sigemptyset( &empty_sigset ) );
73 900 : }
74 :
75 34032 : void clear_got_signal( void )
76 : {
77 900 : for ( volatile sig_atomic_t *p = got_signal;
78 2246112 : p < got_signal + sizeof( got_signal ) / sizeof( *got_signal );
79 : p++ ) {
80 2212080 : *p = 0;
81 : }
82 : }
83 :
84 : /* not implemented */
85 : Select( const Select & );
86 : Select &operator=( const Select & );
87 :
88 : public:
89 65241 : void add_fd( int fd )
90 : {
91 65241 : if ( fd > max_fd ) {
92 1352 : max_fd = fd;
93 : }
94 65241 : FD_SET( fd, &all_fds );
95 65241 : }
96 :
97 : void clear_fds( void )
98 : {
99 563244 : FD_ZERO( &all_fds );
100 : }
101 :
102 4044 : static void add_signal( int signum )
103 : {
104 4044 : fatal_assert( signum >= 0 );
105 4044 : fatal_assert( signum <= MAX_SIGNAL_NUMBER );
106 :
107 : /* Block the signal so we don't get it outside of pselect(). */
108 4044 : sigset_t to_block;
109 4044 : fatal_assert( 0 == sigemptyset( &to_block ) );
110 4044 : fatal_assert( 0 == sigaddset( &to_block, signum ) );
111 4044 : fatal_assert( 0 == sigprocmask( SIG_BLOCK, &to_block, NULL ) );
112 :
113 : /* Register a handler, which will only be called when pselect()
114 : is interrupted by a (possibly queued) signal. */
115 4044 : struct sigaction sa;
116 4044 : sa.sa_flags = 0;
117 4044 : sa.sa_handler = &handle_signal;
118 4044 : fatal_assert( 0 == sigfillset( &sa.sa_mask ) );
119 4044 : fatal_assert( 0 == sigaction( signum, &sa, NULL ) );
120 4044 : }
121 :
122 : /* timeout unit: milliseconds; negative timeout means wait forever */
123 33132 : int select( int timeout )
124 : {
125 33132 : memcpy( &read_fds, &all_fds, sizeof( read_fds ) );
126 33132 : clear_got_signal();
127 :
128 : /* Rate-limit and warn about polls. */
129 33132 : if ( verbose > 1 && timeout == 0 ) {
130 2 : fprintf( stderr, "%s: got poll (timeout 0)\n", __func__ );
131 : }
132 33132 : if ( timeout == 0 && ++consecutive_polls >= MAX_POLLS ) {
133 0 : if ( verbose > 1 && consecutive_polls == MAX_POLLS ) {
134 0 : fprintf( stderr, "%s: got %d polls, rate limiting.\n", __func__, MAX_POLLS );
135 : }
136 : timeout = 1;
137 33132 : } else if ( timeout != 0 && consecutive_polls ) {
138 448 : if ( verbose > 1 && consecutive_polls >= MAX_POLLS ) {
139 0 : fprintf( stderr, "%s: got %d consecutive polls\n", __func__, consecutive_polls );
140 : }
141 448 : consecutive_polls = 0;
142 : }
143 :
144 : #ifdef HAVE_PSELECT
145 33132 : struct timespec ts;
146 66264 : struct timespec *tsp = NULL;
147 :
148 33132 : if ( timeout >= 0 ) {
149 33132 : ts.tv_sec = timeout / 1000;
150 33132 : ts.tv_nsec = 1000000 * (long( timeout ) % 1000);
151 33132 : tsp = &ts;
152 : }
153 :
154 33132 : int ret = ::pselect( max_fd + 1, &read_fds, NULL, NULL, tsp, &empty_sigset );
155 : #else
156 : struct timeval tv;
157 : struct timeval *tvp = NULL;
158 : sigset_t old_sigset;
159 :
160 : if ( timeout >= 0 ) {
161 : tv.tv_sec = timeout / 1000;
162 : tv.tv_usec = 1000 * (long( timeout ) % 1000);
163 : tvp = &tv;
164 : }
165 :
166 : int ret = sigprocmask( SIG_SETMASK, &empty_sigset, &old_sigset );
167 : if ( ret != -1 ) {
168 : ret = ::select( max_fd + 1, &read_fds, NULL, NULL, tvp );
169 : sigprocmask( SIG_SETMASK, &old_sigset, NULL );
170 : }
171 : #endif
172 :
173 33132 : if ( ret == 0 || ( ret == -1 && errno == EINTR ) ) {
174 : /* Look for and report Cygwin select() bug. */
175 5574 : if ( ret == 0 ) {
176 36656 : for ( int fd = 0; fd <= max_fd; fd++ ) {
177 31128 : if ( FD_ISSET( fd, &read_fds ) ) {
178 31128 : fprintf( stderr, "select(): nfds = 0 but read fd %d is set\n", fd );
179 : }
180 : }
181 : }
182 : /* The user should process events as usual. */
183 94758 : FD_ZERO( &read_fds );
184 : ret = 0;
185 : }
186 :
187 33132 : freeze_timestamp();
188 :
189 33132 : return ret;
190 : }
191 :
192 65241 : bool read( int fd )
193 : #if FD_ISSET_IS_CONST
194 : const
195 : #endif
196 : {
197 65241 : assert( FD_ISSET( fd, &all_fds ) );
198 65241 : return FD_ISSET( fd, &read_fds );
199 : }
200 :
201 : /* This method consumes a signal notification. */
202 63572 : bool signal( int signum )
203 : {
204 63572 : fatal_assert( signum >= 0 );
205 63572 : fatal_assert( signum <= MAX_SIGNAL_NUMBER );
206 : /* XXX This requires a guard against concurrent signals. */
207 63572 : bool rv = got_signal[ signum ];
208 63572 : got_signal[ signum ] = 0;
209 33176 : return rv;
210 : }
211 :
212 : /* This method does not consume signal notifications. */
213 27044 : bool any_signal( void ) const
214 : {
215 27044 : bool rv = false;
216 1757860 : for (int i = 0; i < MAX_SIGNAL_NUMBER; i++) {
217 1730816 : rv |= got_signal[ i ];
218 : }
219 27044 : return rv;
220 : }
221 :
222 900 : static void set_verbose( unsigned int s_verbose ) { verbose = s_verbose; }
223 :
224 : private:
225 : static const int MAX_SIGNAL_NUMBER = 64;
226 : /* Number of 0-timeout selects after which we begin to think
227 : * something's wrong. */
228 : static const int MAX_POLLS = 10;
229 :
230 : static void handle_signal( int signum );
231 :
232 : int max_fd;
233 :
234 : /* We assume writes to got_signal are atomic, though we also try to mask out
235 : concurrent signal handlers. */
236 : volatile sig_atomic_t got_signal[ MAX_SIGNAL_NUMBER + 1 ];
237 :
238 : fd_set all_fds, read_fds;
239 :
240 : sigset_t empty_sigset;
241 :
242 : static fd_set dummy_fd_set;
243 : static sigset_t dummy_sigset;
244 : int consecutive_polls;
245 : static unsigned int verbose;
246 : };
247 :
248 : #endif
|