]> git.sur5r.net Git - openldap/blob - servers/slapd/daemon.c
Improved "closing" handling. Remove fd from read set when state
[openldap] / servers / slapd / daemon.c
1 #include "portable.h"
2
3 #include <stdio.h>
4
5 #include <ac/ctype.h>
6 #include <ac/errno.h>
7 #include <ac/signal.h>
8 #include <ac/socket.h>
9 #include <ac/string.h>
10 #include <ac/time.h>
11 #include <ac/unistd.h>
12
13 #include "ldapconfig.h"
14 #include "slap.h"
15
16 #ifdef HAVE_SYS_FILIO_H
17 #include <sys/filio.h>
18 #elif HAVE_SYS_IOCTL_H
19 #include <sys/ioctl.h>
20 #endif
21
22 #ifdef HAVE_TCPD
23 #include <tcpd.h>
24
25 int allow_severity = LOG_INFO;
26 int deny_severity = LOG_NOTICE;
27 #endif /* TCP Wrappers */
28
29 /* globals */
30 int dtblsize;
31
32 static ldap_pvt_thread_t        listener_tid;
33 static volatile sig_atomic_t slapd_shutdown = 0;
34
35 struct slap_daemon {
36         ldap_pvt_thread_mutex_t sd_mutex;
37
38         int sd_nactives;
39
40 #ifndef HAVE_WINSOCK
41         /* In winsock, accept() returns values higher than dtblsize
42                 so don't bother with this optimization */
43         int sd_nfds;
44 #endif
45
46         fd_set sd_actives;
47         fd_set sd_readers;
48         fd_set sd_writers;
49 } slap_daemon; 
50
51 /*
52  * Add a descriptor to daemon control
53  */
54 static void slapd_add(int s) {
55         ldap_pvt_thread_mutex_lock( &slap_daemon.sd_mutex );
56
57         assert( !FD_ISSET( s, &slap_daemon.sd_actives ));
58         assert( !FD_ISSET( s, &slap_daemon.sd_readers ));
59         assert( !FD_ISSET( s, &slap_daemon.sd_writers ));
60
61 #ifndef HAVE_WINSOCK
62         if (s >= slap_daemon.sd_nfds) {
63                 slap_daemon.sd_nfds = s + 1;
64         }
65 #endif
66
67         FD_SET( s, &slap_daemon.sd_actives );
68         FD_SET( s, &slap_daemon.sd_readers );
69
70         Debug( LDAP_DEBUG_CONNS, "daemon: added %d%s%s\n", s,
71             FD_ISSET(s, &slap_daemon.sd_readers) ? "r" : "",
72                 FD_ISSET(s, &slap_daemon.sd_writers) ? "w" : "" );
73
74         ldap_pvt_thread_mutex_unlock( &slap_daemon.sd_mutex );
75 }
76
77 /*
78  * Remove the descriptor from daemon control
79  */
80 void slapd_remove(int s) {
81         ldap_pvt_thread_mutex_lock( &slap_daemon.sd_mutex );
82
83         assert( FD_ISSET( s, &slap_daemon.sd_actives ));
84
85         Debug( LDAP_DEBUG_CONNS, "daemon: removing %d%s%s\n", s,
86             FD_ISSET(s, &slap_daemon.sd_readers) ? "r" : "",
87                 FD_ISSET(s, &slap_daemon.sd_writers) ? "w" : "" );
88
89         FD_CLR( s, &slap_daemon.sd_actives );
90         FD_CLR( s, &slap_daemon.sd_readers );
91         FD_CLR( s, &slap_daemon.sd_writers );
92
93         ldap_pvt_thread_mutex_unlock( &slap_daemon.sd_mutex );
94 }
95
96 void slapd_clr_write(int s, int wake) {
97         ldap_pvt_thread_mutex_lock( &slap_daemon.sd_mutex );
98
99         assert( FD_ISSET( s, &slap_daemon.sd_actives) );
100         FD_CLR( s, &slap_daemon.sd_writers );
101
102         ldap_pvt_thread_mutex_unlock( &slap_daemon.sd_mutex );
103
104         if( wake ) {
105                 ldap_pvt_thread_kill( listener_tid, LDAP_SIGUSR1 );
106         }
107 }
108
109 void slapd_set_write(int s, int wake) {
110         ldap_pvt_thread_mutex_lock( &slap_daemon.sd_mutex );
111
112         assert( FD_ISSET( s, &slap_daemon.sd_actives) );
113         FD_SET( s, &slap_daemon.sd_writers );
114
115         ldap_pvt_thread_mutex_unlock( &slap_daemon.sd_mutex );
116
117         if( wake ) {
118                 ldap_pvt_thread_kill( listener_tid, LDAP_SIGUSR1 );
119         }
120 }
121
122 void slapd_clr_read(int s, int wake) {
123         ldap_pvt_thread_mutex_lock( &slap_daemon.sd_mutex );
124
125         assert( FD_ISSET( s, &slap_daemon.sd_actives) );
126         FD_CLR( s, &slap_daemon.sd_readers );
127
128         ldap_pvt_thread_mutex_unlock( &slap_daemon.sd_mutex );
129
130         if( wake ) {
131                 ldap_pvt_thread_kill( listener_tid, LDAP_SIGUSR1 );
132         }
133 }
134
135 void slapd_set_read(int s, int wake) {
136         ldap_pvt_thread_mutex_lock( &slap_daemon.sd_mutex );
137
138         assert( FD_ISSET( s, &slap_daemon.sd_actives) );
139         FD_SET( s, &slap_daemon.sd_readers );
140
141         ldap_pvt_thread_mutex_unlock( &slap_daemon.sd_mutex );
142
143         if( wake ) {
144                 ldap_pvt_thread_kill( listener_tid, LDAP_SIGUSR1 );
145         }
146 }
147
148 static void slapd_close(int s) {
149         slapd_remove(s);
150
151         Debug( LDAP_DEBUG_CONNS, "daemon: closing %d\n", s, 0, 0 );
152         close(s);
153 }
154
155 static void *
156 slapd_daemon_task(
157         void *ptr
158 )
159 {
160         struct sockaddr_in *addr = ptr;
161         int     tcps = -1;
162
163 #ifdef HAVE_SYSCONF
164         dtblsize = sysconf( _SC_OPEN_MAX );
165 #elif HAVE_GETDTABLESIZE
166         dtblsize = getdtablesize();
167 #else
168         dtblsize = FD_SETSIZE
169 #endif
170
171 #ifdef FD_SETSIZE
172         if(dtblsize > FD_SETSIZE) {
173                 dtblsize = FD_SETSIZE;
174         }
175 #endif  /* !FD_SETSIZE */
176
177         connections_init();
178
179         ldap_pvt_thread_mutex_init( &slap_daemon.sd_mutex );
180         FD_ZERO( &slap_daemon.sd_readers );
181         FD_ZERO( &slap_daemon.sd_writers );
182
183         if( addr != NULL ) {
184                 int     tmp;
185
186                 if ( (tcps = socket( AF_INET, SOCK_STREAM, 0 )) == -1 ) {
187                         Debug( LDAP_DEBUG_ANY,
188                                 "daemon: socket() failed errno %d (%s)", errno,
189                         errno > -1 && errno < sys_nerr ? sys_errlist[errno] :
190                         "unknown", 0 );
191                         exit( 1 );
192                 }
193
194 #ifndef HAVE_WINSOCK
195                 if ( tcps >= dtblsize ) {
196                         Debug( LDAP_DEBUG_ANY,
197                                 "daemon: listener descriptor %d is too great",
198                                 tcps, dtblsize, 0 );
199                         exit( 1 );
200                 }
201 #endif
202
203                 tmp = 1;
204                 if ( setsockopt( tcps, SOL_SOCKET, SO_REUSEADDR,
205                         (char *) &tmp, sizeof(tmp) ) == -1 )
206                 {
207                         Debug( LDAP_DEBUG_ANY,
208                                 "slapd(%d): setsockopt() failed errno %d (%s)",
209                         tcps, errno,
210                                 errno > -1 && errno < sys_nerr
211                                         ? sys_errlist[errno] : "unknown" );
212
213                         errno = 0;
214                 }
215
216                 if ( bind( tcps, (struct sockaddr *) addr, sizeof(*addr) ) == -1 ) {
217                         Debug( LDAP_DEBUG_ANY, "daemon: bind(%d) failed errno %d (%s)\n",
218                         tcps, errno,
219                                 errno > -1 && errno < sys_nerr
220                                         ? sys_errlist[errno] : "unknown" );
221                         exit( 1 );
222                 }
223
224                 if ( listen( tcps, 5 ) == -1 ) {
225                         Debug( LDAP_DEBUG_ANY,
226                                 "daemon: listen(%d, 5) failed errno %d (%s)\n",
227                             tcps, errno,
228                                 errno > -1 && errno < sys_nerr
229                                         ? sys_errlist[errno] : "unknown" );
230                         exit( 1 );
231                 }
232
233                 slapd_add( tcps );
234
235         } else {
236                 if( connection_init( 0, NULL, NULL ) ) {
237                         Debug( LDAP_DEBUG_ANY,
238                                 "connection_init(%d) failed.\n",
239                                 0, 0, 0 );
240
241                         exit( 1 );
242                 }
243
244                 slapd_add( 0 );
245         }
246
247         while ( !slapd_shutdown ) {
248                 int i, ns, nfds;
249
250                 fd_set                  readfds;
251                 fd_set                  writefds;
252
253                 struct sockaddr_in      from;
254                 struct hostent          *hp;
255                 struct timeval          zero;
256                 struct timeval          *tvp;
257
258                 char    *client_name;
259                 char    *client_addr;
260
261                 FD_ZERO( &writefds );
262                 FD_ZERO( &readfds );
263
264                 zero.tv_sec = 0;
265                 zero.tv_usec = 0;
266
267                 ldap_pvt_thread_mutex_lock( &slap_daemon.sd_mutex );
268
269 #ifdef FD_SET_MANUAL_COPY
270                 for( s = 0; s < nfds; s++ ) {
271                         if(FD_ISSET( &slap_sd_writers, s )) {
272                                 FD_SET( &writefds, s );
273                         }
274                         if(FD_ISSET( &slap_sd_writers, s )) {
275                                 FD_SET( &writefds, s );
276                         }
277                 }
278 #else
279                 memcpy( &readfds, &slap_daemon.sd_readers, sizeof(fd_set) );
280                 memcpy( &writefds, &slap_daemon.sd_writers, sizeof(fd_set) );
281 #endif
282
283                 FD_SET( tcps, &readfds );
284
285 #ifndef HAVE_WINSOCK
286                 nfds = slap_daemon.sd_nfds;
287 #else
288                 nfds = dtblsize;
289 #endif
290
291                 ldap_pvt_thread_mutex_unlock( &slap_daemon.sd_mutex );
292
293                 ldap_pvt_thread_mutex_lock( &active_threads_mutex );
294 #if defined( HAVE_YIELDING_SELECT ) || defined( NO_THREADS )
295                 tvp = NULL;
296 #else
297                 tvp = active_threads ? &zero : NULL;
298 #endif
299
300                 Debug( LDAP_DEBUG_CONNS,
301                         "daemon: select: tcps=%d active_threads=%d tvp=%s\n",
302                     tcps, active_threads,
303                         tvp == NULL ? "NULL" : "zero" );
304            
305
306                 ldap_pvt_thread_mutex_unlock( &active_threads_mutex );
307
308                 switch(ns = select( nfds, &readfds, &writefds, 0, tvp )) {
309                 case -1:        /* failure - try again */
310                         if( errno != EINTR ) {
311                                 Debug( LDAP_DEBUG_CONNS,
312                                         "daemon: select failed (%d): %s\n",
313                                         errno,
314                                         errno >= 0 && errno < sys_nerr
315                                                 ? sys_errlist[errno] : "unknown",
316                                         0 );
317
318                                 slapd_shutdown = -1;
319                         }
320                         errno = 0;
321                         continue;
322
323                 case 0:         /* timeout - let threads run */
324                         Debug( LDAP_DEBUG_CONNS, "daemon: select timeout - yielding\n",
325                             0, 0, 0 );
326                 ldap_pvt_thread_yield();
327                         continue;
328
329                 default:        /* something happened - deal with it */
330                         Debug( LDAP_DEBUG_CONNS, "daemon: activity on %d descriptors\n",
331                                 ns, 0, 0 );
332                         /* FALL THRU */
333                 }
334
335                 if ( FD_ISSET( tcps, &readfds ) ) {
336                         int s;
337                         int len = sizeof(from);
338                         long id;
339
340                         if ( (s = accept( tcps,
341                                 (struct sockaddr *) &from, &len )) == -1 )
342                         {
343                                 Debug( LDAP_DEBUG_ANY,
344                                     "daemon: accept(%d) failed errno %d (%s)", errno,
345                                     tcps, errno >= 0 && errno < sys_nerr ?
346                                     sys_errlist[errno] : "unknown");
347                                 continue;
348                         }
349
350                         assert( !FD_ISSET( 0, &slap_daemon.sd_actives) );
351                         assert( !FD_ISSET( 0, &slap_daemon.sd_readers) );
352                         assert( !FD_ISSET( 0, &slap_daemon.sd_writers) );
353
354 #ifndef HAVE_WINSOCK
355                         /* make sure descriptor number isn't too great */
356                         if ( s >= dtblsize ) {
357                                 Debug( LDAP_DEBUG_ANY,
358                                         "daemon: %d beyond descriptor table size %d\n",
359                                         s, dtblsize, 0 );
360                                 close(s);
361                                 continue;
362                         }
363 #endif
364                    
365                         Debug( LDAP_DEBUG_CONNS, "daemon: new connection on %d\n",
366                                 s, 0, 0 );
367
368                         len = sizeof(from);
369                         if ( getpeername( s, (struct sockaddr *) &from, &len ) == 0 ) {
370                                 client_addr = inet_ntoa( from.sin_addr );
371
372 #if defined(SLAPD_RLOOKUPS) || defined(HAVE_TCPD)
373                                 hp = gethostbyaddr( (char *)
374                                     &(from.sin_addr.s_addr),
375                                     sizeof(from.sin_addr.s_addr), AF_INET );
376
377                                 if(hp) {
378                                         char *p;
379                                         client_name = hp->h_name;
380
381                                         /* normalize the domain */
382                                         for ( p = client_name; *p; p++ ) {
383                                                 *p = TOLOWER( (unsigned char) *p );
384                                         }
385
386                                 } else {
387                                         client_name = NULL;
388                                 }
389 #else
390                                 client_name = NULL;
391 #endif
392
393                         } else {
394                                 client_name = NULL;;
395                                 client_addr = NULL;
396                         }
397
398 #ifdef HAVE_TCPD
399                         if(!hosts_ctl("slapd",
400                                 client_name != NULL ? client_name : STRING_UNKNOWN,
401                                 client_addr != NULL ? client_addr : STRING_UNKNOWN,
402                                 STRING_UNKNOWN))
403                         {
404                                 /* DENY ACCESS */
405                                 Statslog( LDAP_DEBUG_ANY,
406                                  "fd=%d connection from %s (%s) denied.\n",
407                                         s,
408                                         client_name == NULL ? "unknown" : client_name,
409                                         client_addr == NULL ? "unknown" : client_addr,
410                                   0, 0 );
411
412                                 close(s);
413                                 continue;
414                         }
415 #endif /* HAVE_TCPD */
416
417                         if( (id = connection_init(s, client_name, client_addr)) < 0 ) {
418                                 Debug( LDAP_DEBUG_ANY,
419                                         "daemon: connection_init(%d, %s, %s) failed.\n",
420                                         s,
421                                         client_name == NULL ? "unknown" : client_name,
422                                         client_addr == NULL ? "unknown" : client_addr);
423                                 close(s);
424                                 continue;
425                         }
426
427                         Statslog( LDAP_DEBUG_STATS,
428                                 "daemon: conn=%d fd=%d connection from %s (%s) accepted.\n",
429                                 id, s,
430                                 client_name == NULL ? "unknown" : client_name,
431                                 client_addr == NULL ? "unknown" : client_addr,
432                                 0 );
433
434                         slapd_add( s );
435                         continue;
436                 }
437
438 #ifdef LDAP_DEBUG
439                 Debug( LDAP_DEBUG_CONNS, "daemon: activity on:", 0, 0, 0 );
440
441                 for ( i = 0; i < nfds; i++ ) {
442                         int     a, r, w;
443
444                         r = FD_ISSET( i, &readfds );
445                         w = FD_ISSET( i, &writefds );
446                         if ( i != tcps && (r || w) ) {
447                                 Debug( LDAP_DEBUG_CONNS, " %d%s%s", i,
448                                     r ? "r" : "", w ? "w" : "" );
449                         }
450                 }
451
452                 Debug( LDAP_DEBUG_CONNS, "\n", 0, 0, 0 );
453 #endif
454
455                 /* loop through the writers */
456                 for ( i = 0; i < nfds; i++ ) {
457                         if ( i == tcps ) {
458                                 continue;
459                         }
460
461                         if ( FD_ISSET( i, &writefds ) ) {
462                                 Debug( LDAP_DEBUG_CONNS,
463                                     "daemon: signaling write waiter on %d\n", i, 0, 0 );
464
465                                 assert( FD_ISSET( 0, &slap_daemon.sd_actives) );
466
467                                 /* clear the write flag */
468                                 slapd_clr_write( i, 0 );
469                                 
470                                 if( connection_write( i ) < 0 ) { 
471                                         FD_CLR( i, &readfds );
472                                         slapd_close( i );
473                                 }
474                         }
475                 }
476
477                 for ( i = 0; i < nfds; i++ ) {
478                         if ( i == tcps ) {
479                                 continue;
480                         }
481
482                         if ( FD_ISSET( i, &readfds ) ) {
483                                 Debug( LDAP_DEBUG_CONNS,
484                                     "daemon: read activity on %d\n", i, 0, 0 );
485
486                                 assert( FD_ISSET( i, &slap_daemon.sd_actives) );
487
488                                 if( connection_read( i ) < 0) {
489                                         slapd_close( i );
490                                 }
491                         }
492                 }
493
494                 ldap_pvt_thread_yield();
495         }
496
497         if( slapd_shutdown > 0 ) {
498                 Debug( LDAP_DEBUG_TRACE,
499                         "daemon: shutdown requested and initiated.\n",
500                         0, 0, 0 );
501
502         } else if ( slapd_shutdown < 0 ) {
503                 Debug( LDAP_DEBUG_TRACE,
504                         "daemon: abnormal condition, shutdown initiated.\n",
505                         0, 0, 0 );
506         } else {
507                 Debug( LDAP_DEBUG_TRACE,
508                         "daemon: no active streams, shutdown initiated.\n",
509                         0, 0, 0 );
510         }
511
512         if( tcps >= 0 ) {
513                 close( tcps );
514         }
515
516         ldap_pvt_thread_mutex_lock( &active_threads_mutex );
517         Debug( LDAP_DEBUG_ANY,
518             "slapd shutdown: waiting for %d threads to terminate\n",
519             active_threads, 0, 0 );
520         while ( active_threads > 0 ) {
521                 ldap_pvt_thread_cond_wait(&active_threads_cond, &active_threads_mutex);
522         }
523         ldap_pvt_thread_mutex_unlock( &active_threads_mutex );
524
525         return NULL;
526 }
527
528 int slapd_daemon( struct sockaddr_in *addr )
529 {
530         int status;
531
532         status = ldap_pvt_thread_create( &listener_tid, 0,
533                  slapd_daemon_task, addr );
534
535         if ( status != 0 ) {
536                 Debug( LDAP_DEBUG_ANY,
537                     "listener ldap_pvt_thread_create failed (%d)\n", status, 0, 0 );
538                 return -1;
539         } else {
540                 /* wait for the listener thread to complete */
541                 ldap_pvt_thread_join( listener_tid, (void *) NULL );
542         }
543
544         return 0;
545 }
546
547 void
548 slap_set_shutdown( int sig )
549 {
550         slapd_shutdown = 1;
551         ldap_pvt_thread_kill( listener_tid, LDAP_SIGUSR1 );
552
553         /* reinstall self */
554         (void) SIGNAL( sig, slap_set_shutdown );
555 }
556
557 void
558 slap_do_nothing( int sig )
559 {
560         /* reinstall self */
561         (void) SIGNAL( sig, slap_do_nothing );
562 }