2000-06-13 07:29:36 +04:00
/*
2002-07-15 14:35:28 +04:00
Unix SMB / Netbios implementation .
Version 3.0
2000-06-13 07:29:36 +04:00
Samba select / poll implementation
Copyright ( C ) Andrew Tridgell 1992 - 1998
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
2007-07-09 23:25:36 +04:00
the Free Software Foundation ; either version 3 of the License , or
2000-06-13 07:29:36 +04:00
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
2007-07-10 04:52:41 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2000-06-13 07:29:36 +04:00
*/
# include "includes.h"
2002-02-01 02:26:12 +03:00
/* This is here because it allows us to avoid a nasty race in signal handling.
2000-06-13 07:29:36 +04:00
We need to guarantee that when we get a signal we get out of a select immediately
but doing that involves a race condition . We can avoid the race by getting the
signal handler to write to a pipe that is in the select / poll list
2002-02-01 02:26:12 +03:00
This means all Samba signal handlers should call sys_select_signal ( ) .
2000-06-13 07:29:36 +04:00
*/
2002-02-01 02:26:12 +03:00
2000-06-21 16:14:51 +04:00
static pid_t initialised ;
2000-06-13 07:29:36 +04:00
static int select_pipe [ 2 ] ;
2008-10-14 05:38:34 +04:00
static volatile unsigned pipe_written , pipe_read ;
2000-06-13 07:29:36 +04:00
/*******************************************************************
2002-02-01 02:26:12 +03:00
Call this from all Samba signal handlers if you want to avoid a
nasty signal race condition .
2000-06-13 07:29:36 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2002-02-01 02:26:12 +03:00
2005-06-09 19:20:11 +04:00
void sys_select_signal ( char c )
2000-06-13 07:29:36 +04:00
{
2009-09-21 01:29:34 +04:00
int saved_errno = errno ;
2000-06-13 07:29:36 +04:00
if ( ! initialised ) return ;
if ( pipe_written > pipe_read + 256 ) return ;
if ( write ( select_pipe [ 1 ] , & c , 1 ) = = 1 ) pipe_written + + ;
2009-09-21 01:29:34 +04:00
errno = saved_errno ;
2000-06-13 07:29:36 +04:00
}
/*******************************************************************
2002-02-01 02:26:12 +03:00
Like select ( ) but avoids the signal race using a pipe
it also guuarantees that fds on return only ever contains bits set
for file descriptors that were readable .
2000-06-13 07:29:36 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2002-02-01 02:26:12 +03:00
int sys_select ( int maxfd , fd_set * readfds , fd_set * writefds , fd_set * errorfds , struct timeval * tval )
2000-06-13 07:29:36 +04:00
{
2000-11-17 04:05:50 +03:00
int ret , saved_errno ;
2002-02-01 02:26:12 +03:00
fd_set * readfds2 , readfds_buf ;
2000-06-13 07:29:36 +04:00
2000-06-21 16:14:51 +04:00
if ( initialised ! = sys_getpid ( ) ) {
2008-02-23 12:49:00 +03:00
if ( pipe ( select_pipe ) = = - 1 )
2009-02-12 11:11:38 +03:00
{
DEBUG ( 0 , ( " sys_select: pipe failed (%s) \n " ,
strerror ( errno ) ) ) ;
if ( readfds ! = NULL )
FD_ZERO ( readfds ) ;
if ( writefds ! = NULL )
FD_ZERO ( writefds ) ;
if ( errorfds ! = NULL )
FD_ZERO ( errorfds ) ;
return - 1 ;
}
2001-02-22 20:39:36 +03:00
/*
* These next two lines seem to fix a bug with the Linux
* 2.0 . x kernel ( and probably other UNIXes as well ) where
* the one byte read below can block even though the
* select returned that there is data in the pipe and
* the pipe_written variable was incremented . Thanks to
* HP for finding this one . JRA .
*/
if ( set_blocking ( select_pipe [ 0 ] , 0 ) = = - 1 )
2007-06-16 01:58:49 +04:00
smb_panic ( " select_pipe[0]: O_NONBLOCK failed " ) ;
2001-02-22 20:39:36 +03:00
if ( set_blocking ( select_pipe [ 1 ] , 0 ) = = - 1 )
2007-06-16 01:58:49 +04:00
smb_panic ( " select_pipe[1]: O_NONBLOCK failed " ) ;
2001-02-22 20:39:36 +03:00
2000-09-12 11:24:06 +04:00
initialised = sys_getpid ( ) ;
2000-06-13 07:29:36 +04:00
}
2000-06-21 16:14:51 +04:00
maxfd = MAX ( select_pipe [ 0 ] + 1 , maxfd ) ;
2002-02-01 02:26:12 +03:00
/* If readfds is NULL we need to provide our own set. */
if ( readfds ) {
readfds2 = readfds ;
} else {
readfds2 = & readfds_buf ;
FD_ZERO ( readfds2 ) ;
}
FD_SET ( select_pipe [ 0 ] , readfds2 ) ;
2000-06-13 07:29:36 +04:00
errno = 0 ;
2002-02-01 02:26:12 +03:00
ret = select ( maxfd , readfds2 , writefds , errorfds , tval ) ;
2000-06-13 07:29:36 +04:00
if ( ret < = 0 ) {
2002-02-01 02:26:12 +03:00
FD_ZERO ( readfds2 ) ;
if ( writefds )
FD_ZERO ( writefds ) ;
if ( errorfds )
FD_ZERO ( errorfds ) ;
2005-03-28 07:18:57 +04:00
} else if ( FD_ISSET ( select_pipe [ 0 ] , readfds2 ) ) {
2002-07-15 14:35:28 +04:00
char c ;
saved_errno = errno ;
if ( read ( select_pipe [ 0 ] , & c , 1 ) = = 1 ) {
pipe_read + + ;
2005-03-28 07:18:57 +04:00
/* Mark Weaver <mark-clist@npsl.co.uk> pointed out a critical
fix to ensure we don ' t lose signals . We must always
return - 1 when the select pipe is set , otherwise if another
fd is also ready ( so ret = = 2 ) then we used to eat the
byte in the pipe and lose the signal . JRA .
*/
2000-06-13 07:29:36 +04:00
ret = - 1 ;
2005-06-09 19:20:11 +04:00
#if 0
/* JRA - we can use this to debug the signal messaging... */
DEBUG ( 0 , ( " select got %u signal \n " , ( unsigned int ) c ) ) ;
# endif
2000-06-13 07:29:36 +04:00
errno = EINTR ;
2005-03-28 07:18:57 +04:00
} else {
FD_CLR ( select_pipe [ 0 ] , readfds2 ) ;
ret - - ;
errno = saved_errno ;
2000-06-13 07:29:36 +04:00
}
}
return ret ;
}
/*******************************************************************
2002-02-01 02:26:12 +03:00
Similar to sys_select ( ) but catch EINTR and continue .
This is what sys_select ( ) used to do in Samba .
2000-06-13 07:29:36 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2002-02-01 02:26:12 +03:00
int sys_select_intr ( int maxfd , fd_set * readfds , fd_set * writefds , fd_set * errorfds , struct timeval * tval )
2000-06-13 07:29:36 +04:00
{
int ret ;
2002-02-01 02:26:12 +03:00
fd_set * readfds2 , readfds_buf , * writefds2 , writefds_buf , * errorfds2 , errorfds_buf ;
2005-01-06 03:45:39 +03:00
struct timeval tval2 , * ptval , end_time ;
2002-02-01 02:26:12 +03:00
readfds2 = ( readfds ? & readfds_buf : NULL ) ;
writefds2 = ( writefds ? & writefds_buf : NULL ) ;
errorfds2 = ( errorfds ? & errorfds_buf : NULL ) ;
2005-01-06 03:45:39 +03:00
if ( tval ) {
GetTimeOfDay ( & end_time ) ;
end_time . tv_sec + = tval - > tv_sec ;
end_time . tv_usec + = tval - > tv_usec ;
end_time . tv_sec + = end_time . tv_usec / 1000000 ;
end_time . tv_usec % = 1000000 ;
errno = 0 ;
tval2 = * tval ;
ptval = & tval2 ;
} else {
ptval = NULL ;
}
2000-06-13 07:29:36 +04:00
do {
2002-02-01 02:26:12 +03:00
if ( readfds )
readfds_buf = * readfds ;
if ( writefds )
writefds_buf = * writefds ;
if ( errorfds )
errorfds_buf = * errorfds ;
2005-01-06 03:45:39 +03:00
if ( ptval & & ( errno = = EINTR ) ) {
struct timeval now_time ;
2008-10-14 03:59:36 +04:00
int64_t tdif ;
2005-01-06 03:45:39 +03:00
GetTimeOfDay ( & now_time ) ;
tdif = usec_time_diff ( & end_time , & now_time ) ;
if ( tdif < = 0 ) {
ret = 0 ; /* time expired. */
break ;
}
ptval - > tv_sec = tdif / 1000000 ;
ptval - > tv_usec = tdif % 1000000 ;
}
2002-07-15 14:35:28 +04:00
2005-03-28 07:18:57 +04:00
/* We must use select and not sys_select here. If we use
sys_select we ' d lose the fact a signal occurred when sys_select
read a byte from the pipe . Fix from Mark Weaver
< mark - clist @ npsl . co . uk >
*/
ret = select ( maxfd , readfds2 , writefds2 , errorfds2 , ptval ) ;
2000-06-13 07:29:36 +04:00
} while ( ret = = - 1 & & errno = = EINTR ) ;
2002-02-01 02:26:12 +03:00
if ( readfds )
* readfds = readfds_buf ;
if ( writefds )
* writefds = writefds_buf ;
if ( errorfds )
* errorfds = errorfds_buf ;
2000-06-13 07:29:36 +04:00
return ret ;
}