]> git.sur5r.net Git - openldap/blob - servers/slapd/daemon.c
Implement "quick" shutdown (similiar in behavior to shutdown
[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         ldap_pvt_thread_mutex_init( &slap_daemon.sd_mutex );
236         FD_ZERO( &slap_daemon.sd_readers );
237         FD_ZERO( &slap_daemon.sd_writers );
238
239         if( !inetd ) {
240                 if ( listen( tcps, 5 ) == -1 ) {
241                         int err = errno;
242                         Debug( LDAP_DEBUG_ANY,
243                                 "daemon: listen(%d, 5) failed errno %d (%s)\n",
244                             tcps, err,
245                                 err > -1 && err < sys_nerr
246                                         ? sys_errlist[err] : "unknown" );
247                         exit( 1 );
248                 }
249
250                 slapd_add( tcps );
251
252         } else {
253                 if( connection_init( 0, NULL, NULL ) ) {
254                         Debug( LDAP_DEBUG_ANY,
255                                 "connection_init(%d) failed.\n",
256                                 0, 0, 0 );
257
258                         exit( 1 );
259                 }
260
261                 slapd_add( 0 );
262         }
263
264         while ( !slapd_shutdown ) {
265                 unsigned int i;
266                 int ns, nfds;
267
268                 fd_set                  readfds;
269                 fd_set                  writefds;
270
271                 struct sockaddr_in      from;
272 #if defined(SLAPD_RLOOKUPS) || defined(HAVE_TCPD)
273         struct hostent          *hp;
274 #endif
275         struct timeval          zero;
276                 struct timeval          *tvp;
277
278                 char    *client_name;
279                 char    *client_addr;
280
281                 FD_ZERO( &writefds );
282                 FD_ZERO( &readfds );
283
284                 zero.tv_sec = 0;
285                 zero.tv_usec = 0;
286
287                 ldap_pvt_thread_mutex_lock( &slap_daemon.sd_mutex );
288
289 #ifdef FD_SET_MANUAL_COPY
290                 for( s = 0; s < nfds; s++ ) {
291                         if(FD_ISSET( &slap_sd_writers, s )) {
292                                 FD_SET( &writefds, s );
293                         }
294                         if(FD_ISSET( &slap_sd_writers, s )) {
295                                 FD_SET( &writefds, s );
296                         }
297                 }
298 #else
299                 memcpy( &readfds, &slap_daemon.sd_readers, sizeof(fd_set) );
300                 memcpy( &writefds, &slap_daemon.sd_writers, sizeof(fd_set) );
301 #endif
302
303                 FD_SET( tcps, &readfds );
304
305 #ifndef HAVE_WINSOCK
306                 nfds = slap_daemon.sd_nfds;
307 #else
308                 nfds = dtblsize;
309 #endif
310
311                 ldap_pvt_thread_mutex_unlock( &slap_daemon.sd_mutex );
312
313                 ldap_pvt_thread_mutex_lock( &active_threads_mutex );
314 #if defined( HAVE_YIELDING_SELECT ) || defined( NO_THREADS )
315                 tvp = NULL;
316 #else
317                 tvp = active_threads ? &zero : NULL;
318 #endif
319
320                 Debug( LDAP_DEBUG_CONNS,
321                         "daemon: select: tcps=%d active_threads=%d tvp=%s\n",
322                     tcps, active_threads,
323                         tvp == NULL ? "NULL" : "zero" );
324            
325
326                 ldap_pvt_thread_mutex_unlock( &active_threads_mutex );
327
328                 switch(ns = select( nfds, &readfds, &writefds, 0, tvp )) {
329                 case -1: {      /* failure - try again */
330                                 int err = errno;
331                                 if( err != EINTR ) {
332                                         Debug( LDAP_DEBUG_CONNS,
333                                                 "daemon: select failed (%d): %s\n",
334                                                 err,
335                                                 err >= 0 && err < sys_nerr
336                                                         ? sys_errlist[err] : "unknown",
337                                                 0 );
338
339                                         slapd_shutdown = -1;
340                                 }
341                         }
342                         continue;
343
344                 case 0:         /* timeout - let threads run */
345                         Debug( LDAP_DEBUG_CONNS, "daemon: select timeout - yielding\n",
346                             0, 0, 0 );
347                 ldap_pvt_thread_yield();
348                         continue;
349
350                 default:        /* something happened - deal with it */
351                         Debug( LDAP_DEBUG_CONNS, "daemon: activity on %d descriptors\n",
352                                 ns, 0, 0 );
353                         /* FALL THRU */
354                 }
355
356                 if ( FD_ISSET( tcps, &readfds ) ) {
357                         int s;
358                         int len = sizeof(from);
359                         long id;
360
361                         if ( (s = accept( tcps,
362                                 (struct sockaddr *) &from, &len )) == -1 )
363                         {
364                                 int err = errno;
365                                 Debug( LDAP_DEBUG_ANY,
366                                     "daemon: accept(%d) failed errno %d (%s)\n", err,
367                                     tcps, err >= 0 && err < sys_nerr ?
368                                     sys_errlist[err] : "unknown");
369                                 continue;
370                         }
371
372                         assert( !FD_ISSET( 0, &slap_daemon.sd_actives) );
373                         assert( !FD_ISSET( 0, &slap_daemon.sd_readers) );
374                         assert( !FD_ISSET( 0, &slap_daemon.sd_writers) );
375
376 #ifndef HAVE_WINSOCK
377                         /* make sure descriptor number isn't too great */
378                         if ( s >= dtblsize ) {
379                                 Debug( LDAP_DEBUG_ANY,
380                                         "daemon: %d beyond descriptor table size %d\n",
381                                         s, dtblsize, 0 );
382                                 tcp_close(s);
383                                 continue;
384                         }
385 #endif
386                    
387                         Debug( LDAP_DEBUG_CONNS, "daemon: new connection on %d\n",
388                                 s, 0, 0 );
389
390                         len = sizeof(from);
391                         if ( getpeername( s, (struct sockaddr *) &from, &len ) == 0 ) {
392                                 client_addr = inet_ntoa( from.sin_addr );
393
394 #if defined(SLAPD_RLOOKUPS) || defined(HAVE_TCPD)
395                                 hp = gethostbyaddr( (char *)
396                                     &(from.sin_addr.s_addr),
397                                     sizeof(from.sin_addr.s_addr), AF_INET );
398
399                                 if(hp) {
400                                         char *p;
401                                         client_name = hp->h_name;
402
403                                         /* normalize the domain */
404                                         for ( p = client_name; *p; p++ ) {
405                                                 *p = TOLOWER( (unsigned char) *p );
406                                         }
407
408                                 } else {
409                                         client_name = NULL;
410                                 }
411 #else
412                                 client_name = NULL;
413 #endif
414
415                         } else {
416                                 client_name = NULL;;
417                                 client_addr = NULL;
418                         }
419
420 #ifdef HAVE_TCPD
421                         if(!hosts_ctl("slapd",
422                                 client_name != NULL ? client_name : STRING_UNKNOWN,
423                                 client_addr != NULL ? client_addr : STRING_UNKNOWN,
424                                 STRING_UNKNOWN))
425                         {
426                                 /* DENY ACCESS */
427                                 Statslog( LDAP_DEBUG_ANY,
428                                  "fd=%d connection from %s (%s) denied.\n",
429                                         s,
430                                         client_name == NULL ? "unknown" : client_name,
431                                         client_addr == NULL ? "unknown" : client_addr,
432                                   0, 0 );
433
434                                 tcp_close(s);
435                                 continue;
436                         }
437 #endif /* HAVE_TCPD */
438
439                         if( (id = connection_init(s, client_name, client_addr)) < 0 ) {
440                                 Debug( LDAP_DEBUG_ANY,
441                                         "daemon: connection_init(%d, %s, %s) failed.\n",
442                                         s,
443                                         client_name == NULL ? "unknown" : client_name,
444                                         client_addr == NULL ? "unknown" : client_addr);
445                                 tcp_close(s);
446                                 continue;
447                         }
448
449                         Statslog( LDAP_DEBUG_STATS,
450                                 "daemon: conn=%d fd=%d connection from %s (%s) accepted.\n",
451                                 id, s,
452                                 client_name == NULL ? "unknown" : client_name,
453                                 client_addr == NULL ? "unknown" : client_addr,
454                                 0 );
455
456                         slapd_add( s );
457                         continue;
458                 }
459
460 #ifdef LDAP_DEBUG
461                 Debug( LDAP_DEBUG_CONNS, "daemon: activity on:", 0, 0, 0 );
462 #ifdef HAVE_WINSOCK
463                 for ( i = 0; i < readfds.fd_count; i++ )
464                 {
465                         Debug( LDAP_DEBUG_CONNS, " %d%s", readfds.fd_array[i], "r" );
466                 }
467                 for ( i = 0; i < writefds.fd_count; i++ )
468                 {
469                         Debug( LDAP_DEBUG_CONNS, " %d%s", writefds.fd_array[i], "w" );
470                 }
471 #else
472                 for ( i = 0; i < nfds; i++ ) {
473                         int     a, r, w;
474
475                         r = FD_ISSET( i, &readfds );
476                         w = FD_ISSET( i, &writefds );
477                         if ( i != tcps && (r || w) ) {
478                                 Debug( LDAP_DEBUG_CONNS, " %d%s%s", i,
479                                     r ? "r" : "", w ? "w" : "" );
480                         }
481                 }
482 #endif
483                 Debug( LDAP_DEBUG_CONNS, "\n", 0, 0, 0 );
484 #endif
485
486                 /* loop through the writers */
487 #ifdef HAVE_WINSOCK
488                 for ( i = 0; i < writefds.fd_count; i++ ) {
489                         int wd = writefds.fd_array[i];
490
491                         if ( wd == tcps ) {
492                                 continue;
493                         }
494
495                         Debug( LDAP_DEBUG_CONNS,
496                                 "daemon: signalling write waiter on %d\n",
497                                 wd, 0, 0 );
498
499                         assert( FD_ISSET( wd, &slap_daemon.sd_actives) );
500
501                         slapd_clr_write( wd, 0 );
502                         if ( connection_write( wd ) < 0 ) {
503                                 FD_CLR( wd, &readfds );
504                                 slapd_close( wd );
505                         }
506                 }
507 #else
508                 for ( i = 0; i < nfds; i++ ) {
509                         if ( i == tcps ) {
510                                 continue;
511                         }
512                         if ( FD_ISSET( i, &writefds ) ) {
513                                 Debug( LDAP_DEBUG_CONNS,
514                                     "daemon: signaling write waiter on %d\n", i, 0, 0 );
515
516                                 assert( FD_ISSET( i, &slap_daemon.sd_actives) );
517
518                                 /* clear the write flag */
519                                 slapd_clr_write( i, 0 );
520                                 
521                                 if( connection_write( i ) < 0 ) { 
522                                         FD_CLR( i, &readfds );
523                                         slapd_close( i );
524                                 }
525                         }
526                 }
527 #endif
528
529 #ifdef HAVE_WINSOCK
530                 for ( i = 0; i < readfds.fd_count; i++ ) {
531                         int rd = readfds.fd_array[i];
532                         if ( rd == tcps ) {
533                                 continue;
534                         }
535                         Debug ( LDAP_DEBUG_CONNS,
536                                 "daemon: read activity on %d\n", rd, 0, 0 );
537                         assert( FD_ISSET( rd, &slap_daemon.sd_actives) );
538
539                         if ( connection_read( rd ) < 0 ) {
540                                 slapd_close( rd );
541                         }
542                 }
543 #else
544                 for ( i = 0; i < nfds; i++ ) {
545                         if ( i == tcps ) {
546                                 continue;
547                         }
548
549                         if ( FD_ISSET( i, &readfds ) ) {
550                                 Debug( LDAP_DEBUG_CONNS,
551                                     "daemon: read activity on %d\n", i, 0, 0 );
552
553                                 assert( FD_ISSET( i, &slap_daemon.sd_actives) );
554
555                                 if( connection_read( i ) < 0) {
556                                         slapd_close( i );
557                                 }
558                         }
559                 }
560 #endif
561                 ldap_pvt_thread_yield();
562         }
563
564         if( slapd_shutdown > 0 ) {
565                 Debug( LDAP_DEBUG_TRACE,
566                         "daemon: shutdown requested (%d) and initiated.\n",
567                         (int) slapd_shutdown, 0, 0 );
568
569         } else if ( slapd_shutdown < 0 ) {
570                 Debug( LDAP_DEBUG_TRACE,
571                         "daemon: abnormal condition (%d), shutdown initiated.\n",
572                         (int) slapd_shutdown, 0, 0 );
573         } else {
574                 Debug( LDAP_DEBUG_TRACE,
575                         "daemon: no active streams, shutdown initiated.\n",
576                         0, 0, 0 );
577         }
578
579         if( tcps >= 0 ) {
580                 tcp_close( tcps );
581         }
582
583         /* we only implement "quick" shutdown */
584         connections_shutdown();
585
586         ldap_pvt_thread_mutex_lock( &active_threads_mutex );
587         Debug( LDAP_DEBUG_ANY,
588             "slapd shutdown: waiting for %d threads to terminate\n",
589             active_threads, 0, 0 );
590         while ( active_threads > 0 ) {
591                 ldap_pvt_thread_cond_wait(&active_threads_cond, &active_threads_mutex);
592         }
593         ldap_pvt_thread_mutex_unlock( &active_threads_mutex );
594
595         slapd_listener = 0;
596         return NULL;
597 }
598
599 int slapd_daemon( int inetd, int tcps )
600 {
601         int status;
602         int *args = ch_malloc( sizeof( int[2] ) );
603         args[0] = inetd;
604         args[1] = tcps;
605
606         connections_init();
607
608 #define SLAPD_LISTENER_THREAD 1
609 #if SLAPD_LISTENER_THREAD
610         /* listener as a separate THREAD */
611         status = ldap_pvt_thread_create( &listener_tid,
612                 0, slapd_daemon_task, args );
613
614         if ( status != 0 ) {
615                 Debug( LDAP_DEBUG_ANY,
616                     "listener ldap_pvt_thread_create failed (%d)\n", status, 0, 0 );
617                 return -1;
618         }
619
620         /* wait for the listener thread to complete */
621         ldap_pvt_thread_join( listener_tid, (void *) NULL );
622 #else
623         /* expermimental code */
624         listener_tid = pthread_self();
625         slapd_daemon_task( args );
626 #endif
627
628         connections_destroy();
629         return 0;
630 }
631
632 void
633 slap_set_shutdown( int sig )
634 {
635         slapd_shutdown = sig;
636
637         if(slapd_listener) {
638                 ldap_pvt_thread_kill( listener_tid, LDAP_SIGUSR1 );
639         }
640
641         /* reinstall self */
642         (void) SIGNAL( sig, slap_set_shutdown );
643 }
644
645 void
646 slap_do_nothing( int sig )
647 {
648         /* reinstall self */
649         (void) SIGNAL( sig, slap_do_nothing );
650 }