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