1/* 2 Unix SMB/Netbios implementation. 3 Version 3.0 4 Samba select/poll implementation 5 Copyright (C) Andrew Tridgell 1992-1998 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program; if not, write to the Free Software 19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20*/ 21 22#include "includes.h" 23 24/* This is here because it allows us to avoid a nasty race in signal handling. 25 We need to guarantee that when we get a signal we get out of a select immediately 26 but doing that involves a race condition. We can avoid the race by getting the 27 signal handler to write to a pipe that is in the select/poll list 28 29 This means all Samba signal handlers should call sys_select_signal(). 30*/ 31 32static pid_t initialised; 33static int select_pipe[2]; 34static VOLATILE unsigned pipe_written, pipe_read; 35 36/******************************************************************* 37 Call this from all Samba signal handlers if you want to avoid a 38 nasty signal race condition. 39********************************************************************/ 40 41void sys_select_signal(void) 42{ 43 char c = 1; 44 if (!initialised) return; 45 46 if (pipe_written > pipe_read+256) return; 47 48 if (write(select_pipe[1], &c, 1) == 1) pipe_written++; 49} 50 51/******************************************************************* 52 Like select() but avoids the signal race using a pipe 53 it also guuarantees that fds on return only ever contains bits set 54 for file descriptors that were readable. 55********************************************************************/ 56 57int sys_select(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *tval) 58{ 59 int ret, saved_errno; 60 fd_set *readfds2, readfds_buf; 61 62 if (initialised != sys_getpid()) { 63 pipe(select_pipe); 64 65 /* 66 * These next two lines seem to fix a bug with the Linux 67 * 2.0.x kernel (and probably other UNIXes as well) where 68 * the one byte read below can block even though the 69 * select returned that there is data in the pipe and 70 * the pipe_written variable was incremented. Thanks to 71 * HP for finding this one. JRA. 72 */ 73 74 if(set_blocking(select_pipe[0],0)==-1) 75 smb_panic("select_pipe[0]: O_NONBLOCK failed.\n"); 76 if(set_blocking(select_pipe[1],0)==-1) 77 smb_panic("select_pipe[1]: O_NONBLOCK failed.\n"); 78 79 initialised = sys_getpid(); 80 } 81 82 maxfd = MAX(select_pipe[0]+1, maxfd); 83 84 /* If readfds is NULL we need to provide our own set. */ 85 if (readfds) { 86 readfds2 = readfds; 87 } else { 88 readfds2 = &readfds_buf; 89 FD_ZERO(readfds2); 90 } 91 FD_SET(select_pipe[0], readfds2); 92 93 errno = 0; 94 ret = select(maxfd,readfds2,writefds,errorfds,tval); 95 96 if (ret <= 0) { 97 FD_ZERO(readfds2); 98 if (writefds) 99 FD_ZERO(writefds); 100 if (errorfds) 101 FD_ZERO(errorfds); 102 } 103 104 if (FD_ISSET(select_pipe[0], readfds2)) { 105 char c; 106 saved_errno = errno; 107 if (read(select_pipe[0], &c, 1) == 1) { 108 pipe_read++; 109 } 110 errno = saved_errno; 111 FD_CLR(select_pipe[0], readfds2); 112 ret--; 113 if (ret == 0) { 114 ret = -1; 115 errno = EINTR; 116 } 117 } 118 119 return ret; 120} 121 122/******************************************************************* 123 Similar to sys_select() but catch EINTR and continue. 124 This is what sys_select() used to do in Samba. 125********************************************************************/ 126 127int sys_select_intr(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *tval) 128{ 129 int ret; 130 fd_set *readfds2, readfds_buf, *writefds2, writefds_buf, *errorfds2, errorfds_buf; 131 struct timeval tval2, *ptval, end_time; 132 133 readfds2 = (readfds ? &readfds_buf : NULL); 134 writefds2 = (writefds ? &writefds_buf : NULL); 135 errorfds2 = (errorfds ? &errorfds_buf : NULL); 136 if (tval) { 137 GetTimeOfDay(&end_time); 138 end_time.tv_sec += tval->tv_sec; 139 end_time.tv_usec += tval->tv_usec; 140 end_time.tv_sec += end_time.tv_usec / 1000000; 141 end_time.tv_usec %= 1000000; 142 errno = 0; 143 tval2 = *tval; 144 ptval = &tval2; 145 } else { 146 ptval = NULL; 147 } 148 149 do { 150 if (readfds) 151 readfds_buf = *readfds; 152 if (writefds) 153 writefds_buf = *writefds; 154 if (errorfds) 155 errorfds_buf = *errorfds; 156 if (ptval && (errno == EINTR)) { 157 struct timeval now_time; 158 SMB_BIG_INT tdif; 159 160 GetTimeOfDay(&now_time); 161 tdif = usec_time_diff(&end_time, &now_time); 162 if (tdif <= 0) { 163 ret = 0; /* time expired. */ 164 break; 165 } 166 ptval->tv_sec = tdif / 1000000; 167 ptval->tv_usec = tdif % 1000000; 168 } 169 170 ret = sys_select(maxfd, readfds2, writefds2, errorfds2, ptval); 171 } while (ret == -1 && errno == EINTR); 172 173 if (readfds) 174 *readfds = readfds_buf; 175 if (writefds) 176 *writefds = writefds_buf; 177 if (errorfds) 178 *errorfds = errorfds_buf; 179 180 return ret; 181} 182