]> git.sur5r.net Git - openldap/blob - servers/slapd/connection.c
First cut at bind race fix. Passes our test suite at least.
[openldap] / servers / slapd / connection.c
1 #include "portable.h"
2
3 #include <stdio.h>
4
5 #include <ac/errno.h>
6 #include <ac/signal.h>
7 #include <ac/socket.h>
8 #include <ac/string.h>
9 #include <ac/time.h>
10
11 #include "slap.h"
12
13 static int connection_op_activate( Connection *conn, Operation *op );
14 static int connection_resched( Connection *conn );
15
16 struct co_arg {
17         Connection      *co_conn;
18         Operation       *co_op;
19 };
20
21 /*
22  * connection_activity - handle the request operation op on connection
23  * conn.  This routine figures out what kind of operation it is and
24  * calls the appropriate stub to handle it.
25  */
26
27 static void *
28 connection_operation( void *arg_v )
29 {
30         struct co_arg   *arg = arg_v;
31         int tag = arg->co_op->o_tag;
32         Connection *conn = arg->co_conn;
33
34         ldap_pvt_thread_mutex_lock( &conn->c_opsmutex );
35         conn->c_ops_received++;
36         ldap_pvt_thread_mutex_unlock( &conn->c_opsmutex );
37
38         ldap_pvt_thread_mutex_lock( &ops_mutex );
39         ops_initiated++;
40         ldap_pvt_thread_mutex_unlock( &ops_mutex );
41
42         switch ( tag ) {
43         case LDAP_REQ_BIND:
44                 do_bind( conn, arg->co_op );
45                 break;
46
47 #ifdef LDAP_COMPAT30
48         case LDAP_REQ_UNBIND_30:
49 #endif
50         case LDAP_REQ_UNBIND:
51                 do_unbind( conn, arg->co_op );
52                 break;
53
54         case LDAP_REQ_ADD:
55                 do_add( conn, arg->co_op );
56                 break;
57
58 #ifdef LDAP_COMPAT30
59         case LDAP_REQ_DELETE_30:
60 #endif
61         case LDAP_REQ_DELETE:
62                 do_delete( conn, arg->co_op );
63                 break;
64
65         case LDAP_REQ_MODRDN:
66                 do_modrdn( conn, arg->co_op );
67                 break;
68
69         case LDAP_REQ_MODIFY:
70                 do_modify( conn, arg->co_op );
71                 break;
72
73         case LDAP_REQ_COMPARE:
74                 do_compare( conn, arg->co_op );
75                 break;
76
77         case LDAP_REQ_SEARCH:
78                 do_search( conn, arg->co_op );
79                 break;
80
81 #ifdef LDAP_COMPAT30
82         case LDAP_REQ_ABANDON_30:
83 #endif
84         case LDAP_REQ_ABANDON:
85                 do_abandon( conn, arg->co_op );
86                 break;
87
88         default:
89                 Debug( LDAP_DEBUG_ANY, "unknown request 0x%lx\n",
90                     arg->co_op->o_tag, 0, 0 );
91                 break;
92         }
93
94         ldap_pvt_thread_mutex_lock( &ops_mutex );
95         ops_completed++;
96         ldap_pvt_thread_mutex_unlock( &ops_mutex );
97
98         ldap_pvt_thread_mutex_lock( &conn->c_opsmutex );
99         conn->c_ops_completed++;
100
101         slap_op_remove( &conn->c_ops, arg->co_op );
102         slap_op_free( arg->co_op );
103         arg->co_op = NULL;
104         arg->co_conn = NULL;
105         free( (char *) arg );
106         arg = NULL;
107
108         if((tag == LDAP_REQ_BIND) && (conn->c_state == SLAP_C_BINDING)) {
109                 conn->c_state = SLAP_C_ACTIVE;
110         }
111
112         ldap_pvt_thread_mutex_unlock( &conn->c_opsmutex );
113
114         ldap_pvt_thread_mutex_lock( &active_threads_mutex );
115         active_threads--;
116         if( active_threads < 1 ) {
117                 ldap_pvt_thread_cond_signal(&active_threads_cond);
118         }
119         ldap_pvt_thread_mutex_unlock( &active_threads_mutex );
120
121         connection_resched( conn );
122
123         return NULL;
124 }
125
126 void
127 connection_activity(
128     Connection *conn
129 )
130 {
131         Operation *op;
132         unsigned long   tag, len;
133         long            msgid;
134         BerElement      *ber;
135
136         if ( conn->c_currentber == NULL && (conn->c_currentber = ber_alloc())
137             == NULL ) {
138                 Debug( LDAP_DEBUG_ANY, "ber_alloc failed\n", 0, 0, 0 );
139                 return;
140         }
141
142         errno = 0;
143         if ( (tag = ber_get_next( &conn->c_sb, &len, conn->c_currentber ))
144             != LDAP_TAG_MESSAGE ) {
145                 Debug( LDAP_DEBUG_TRACE,
146                     "ber_get_next on fd %d failed errno %d (%s)\n",
147                     lber_pvt_sb_get_desc(&conn->c_sb), errno, errno > -1 && errno < sys_nerr ?
148                     sys_errlist[errno] : "unknown" );
149                 Debug( LDAP_DEBUG_TRACE, "*** got %ld of %lu so far\n",
150                     (long)(conn->c_currentber->ber_rwptr - conn->c_currentber->ber_buf),
151                     conn->c_currentber->ber_len, 0 );
152
153                 if ( errno != EWOULDBLOCK && errno != EAGAIN ) {
154                         /* log, close and send error */
155                         ber_free( conn->c_currentber, 1 );
156                         conn->c_currentber = NULL;
157
158                         close_connection( conn, conn->c_connid, -1 );
159                 }
160
161                 return;
162         }
163         ber = conn->c_currentber;
164         conn->c_currentber = NULL;
165
166         if ( (tag = ber_get_int( ber, &msgid )) != LDAP_TAG_MSGID ) {
167                 /* log, close and send error */
168                 Debug( LDAP_DEBUG_ANY, "ber_get_int returns 0x%lx\n", tag, 0,
169                     0 );
170                 ber_free( ber, 1 );
171
172                 close_connection( conn, conn->c_connid, -1 );
173                 return;
174         }
175
176         if ( (tag = ber_peek_tag( ber, &len )) == LBER_ERROR ) {
177                 /* log, close and send error */
178                 Debug( LDAP_DEBUG_ANY, "ber_peek_tag returns 0x%lx\n", tag, 0,
179                     0 );
180                 ber_free( ber, 1 );
181
182                 close_connection( conn, conn->c_connid, -1 );
183                 return;
184         }
185
186 #ifdef LDAP_COMPAT30
187         if ( conn->c_version == 30 ) {
188                 (void) ber_skip_tag( ber, &len );
189         }
190 #endif
191
192         op = slap_op_alloc( ber, msgid, tag,
193                 conn->c_ops_received, conn->c_connid );
194
195         if ( conn->c_state == SLAP_C_BINDING ) {
196                 /* connection is binding to a dn, make 'em wait */
197                 ldap_pvt_thread_mutex_lock( &conn->c_opsmutex );
198                 slap_op_add( &conn->c_pending_ops, op );
199
200                 Debug( LDAP_DEBUG_ANY, "deferring operation\n", 0, 0, 0 );
201
202                 ldap_pvt_thread_mutex_unlock( &conn->c_opsmutex );
203
204                 return;
205         }
206
207         connection_op_activate( conn, op );
208 }
209
210 static int
211 connection_resched( Connection *conn )
212 {
213         Operation *op;
214
215         if( conn->c_state != SLAP_C_ACTIVE ) {
216                 /* other states need different handling */
217                 return;
218         }
219
220         ldap_pvt_thread_mutex_lock( &conn->c_opsmutex );
221
222         for( op = slap_op_pop( &conn->c_pending_ops );
223                 op != NULL;
224                 op = slap_op_pop( &conn->c_pending_ops ) )
225         {
226                 ldap_pvt_thread_mutex_unlock( &conn->c_opsmutex );
227
228                 connection_op_activate( conn, op );
229
230                 ldap_pvt_thread_mutex_lock( &conn->c_opsmutex );
231
232                 if ( conn->c_state == SLAP_C_BINDING ) {
233                         break;
234                 }
235         }
236
237         ldap_pvt_thread_mutex_unlock( &conn->c_opsmutex );
238 }
239
240 static int connection_op_activate( Connection *conn, Operation *op )
241 {
242         struct co_arg *arg;
243         char *tmpdn;
244         int status;
245         unsigned long tag = op->o_tag;
246
247         ldap_pvt_thread_mutex_lock( &conn->c_dnmutex );
248         if ( conn->c_dn != NULL ) {
249                 tmpdn = ch_strdup( conn->c_dn );
250         } else {
251                 tmpdn = NULL;
252         }
253         ldap_pvt_thread_mutex_unlock( &conn->c_dnmutex );
254
255         arg = (struct co_arg *) ch_malloc( sizeof(struct co_arg) );
256         arg->co_conn = conn;
257         arg->co_op = op;
258
259         arg->co_op->o_dn = ch_strdup( tmpdn != NULL ? tmpdn : "" );
260         arg->co_op->o_ndn = dn_normalize_case( ch_strdup( arg->co_op->o_dn ) );
261
262         ldap_pvt_thread_mutex_lock( &conn->c_opsmutex );
263
264         slap_op_add( &conn->c_ops, arg->co_op );
265
266         if(tag == LDAP_REQ_BIND) {
267                 conn->c_state = SLAP_C_BINDING;
268         }
269
270         ldap_pvt_thread_mutex_unlock( &conn->c_opsmutex );
271
272         if ( tmpdn != NULL ) {
273                 free( tmpdn );
274         }
275
276         ldap_pvt_thread_mutex_lock( &active_threads_mutex );
277         active_threads++;
278         ldap_pvt_thread_mutex_unlock( &active_threads_mutex );
279
280         status = ldap_pvt_thread_create( &arg->co_op->o_tid, 1,
281                                          connection_operation, (void *) arg );
282
283         if ( status != 0 ) {
284                 Debug( LDAP_DEBUG_ANY, "ldap_pvt_thread_create failed (%d)\n", status, 0, 0 );
285         }
286
287         return status;
288 }