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