comparison libfdcore/server.c @ 1214:76ac4bb75f0e

Merged with latest proposed version
author Sebastien Decugis <sdecugis@freediameter.net>
date Mon, 17 Jun 2013 10:11:57 +0800
parents 9ff57791a5ab
children
comparison
equal deleted inserted replaced
1188:e1ced4db7f67 1214:76ac4bb75f0e
53 53
54 struct cnxctx * conn; /* server connection context (listening socket) */ 54 struct cnxctx * conn; /* server connection context (listening socket) */
55 int proto; /* IPPROTO_TCP or IPPROTO_SCTP */ 55 int proto; /* IPPROTO_TCP or IPPROTO_SCTP */
56 int secur; /* TLS is started immediatly after connection ? 0: no; 1: yes (TLS/TCP or DTLS/SCTP); 2: yes (TLS/TCP or TLS/SCTP) */ 56 int secur; /* TLS is started immediatly after connection ? 0: no; 1: yes (TLS/TCP or DTLS/SCTP); 2: yes (TLS/TCP or TLS/SCTP) */
57 57
58 pthread_t thr; /* The thread listening for new connections */ 58 pthread_t thr; /* The thread waiting for new connections (will store the data in the clients fifo) */
59 enum s_state state; /* state of the thread */ 59 enum s_state state; /* state of the thread */
60 60
61 struct fd_list clients; /* List of clients connected to this server, not yet identified */ 61 struct fifo *pending; /* FIFO of struct cnxctx */
62 pthread_mutex_t clients_mtx; /* Mutex to protect the list of clients */ 62 struct pool_workers {
63 struct server * s; /* pointer to the parent server structure */
64 int id; /* The worker id for logs */
65 pthread_t worker; /* The thread */
66 } *workers; /* array of cnf_thr_srv items */
63 }; 67 };
64
65
66 /* Client information (connecting peer for which we don't have the CER yet) */
67 struct client {
68 struct fd_list chain; /* link in the server's list of clients */
69 struct cnxctx *conn; /* Parameters of the connection */
70 struct timespec ts; /* Deadline for receiving CER (after INCNX_TIMEOUT) */
71 pthread_t thr; /* connection state machine */
72 };
73
74 68
75 69
76 /* Micro functions to read/change the status thread-safely */ 70 /* Micro functions to read/change the status thread-safely */
77 static pthread_mutex_t s_lock = PTHREAD_MUTEX_INITIALIZER; 71 static pthread_mutex_t s_lock = PTHREAD_MUTEX_INITIALIZER;
78 static enum s_state get_status(struct server * s) 72 static enum s_state get_status(struct server * s)
89 s->state = st; 83 s->state = st;
90 CHECK_POSIX_DO( pthread_mutex_unlock(&s_lock), return ); 84 CHECK_POSIX_DO( pthread_mutex_unlock(&s_lock), return );
91 } 85 }
92 86
93 87
88 /* dump one item of the server->pending fifo */
89 static DECLARE_FD_DUMP_PROTOTYPE(dump_cnx, void * item) {
90 struct cnxctx * c = item;
91 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " '%s'", fd_cnx_getid(c)), return NULL);
92 return *buf;
93 }
94 94
95 /* Dump all servers information */ 95 /* Dump all servers information */
96 DECLARE_FD_DUMP_PROTOTYPE(fd_servers_dump, int details) 96 DECLARE_FD_DUMP_PROTOTYPE(fd_servers_dump, int details)
97 { 97 {
98 struct fd_list * li, *cli; 98 struct fd_list * li;
99 99
100 FD_DUMP_HANDLE_OFFSET(); 100 FD_DUMP_HANDLE_OFFSET();
101 101
102 for (li = FD_SERVERS.next; li != &FD_SERVERS; li = li->next) { 102 for (li = FD_SERVERS.next; li != &FD_SERVERS; li = li->next) {
103 struct server * s = (struct server *)li; 103 struct server * s = (struct server *)li;
110 (st == NOT_CREATED) ? "Thread not created" : 110 (st == NOT_CREATED) ? "Thread not created" :
111 ((st == RUNNING) ? "Thread running" : 111 ((st == RUNNING) ? "Thread running" :
112 ((st == TERMINATED) ? "Thread terminated" : 112 ((st == TERMINATED) ? "Thread terminated" :
113 "Thread status unknown"))), return NULL); 113 "Thread status unknown"))), return NULL);
114 /* Dump the client list of this server */ 114 /* Dump the client list of this server */
115 CHECK_POSIX_DO( pthread_mutex_lock(&s->clients_mtx), ); 115 CHECK_MALLOC_DO( fd_fifo_dump(FD_DUMP_STD_PARAMS, "pending connections", s->pending, dump_cnx), return NULL );
116 for (cli = s->clients.next; cli != &s->clients; cli = cli->next) {
117 struct client * c = (struct client *)cli;
118 char bufts[128];
119 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n {client}(@%p)'%s': to:%s", c, fd_cnx_getid(c->conn), fd_log_time(&c->ts, bufts, sizeof(bufts))), break);
120 }
121 CHECK_POSIX_DO( pthread_mutex_unlock(&s->clients_mtx), );
122 116
123 if (li->next != &FD_SERVERS) { 117 if (li->next != &FD_SERVERS) {
124 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n"), return NULL); 118 CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n"), return NULL);
125 } 119 }
126 } else { 120 } else {
131 125
132 return *buf; 126 return *buf;
133 } 127 }
134 128
135 129
136 /* The state machine to handle incoming connection before the remote peer is identified */ 130 /* The thread in the pool for handling new clients connecting to a server */
137 static void * client_sm(void * arg) 131 static void * client_worker(void * arg)
138 { 132 {
139 struct client * c = arg; 133 struct pool_workers * pw = arg;
140 struct server * s = NULL; 134 struct server * s = pw->s;
135 struct cnxctx * c = NULL;
136 int fatal = 0;
137 struct timespec ts;
141 struct fd_cnx_rcvdata rcv_data; 138 struct fd_cnx_rcvdata rcv_data;
142 struct fd_msg_pmdl * pmdl = NULL; 139 struct fd_msg_pmdl * pmdl = NULL;
143 struct msg * msg = NULL; 140 struct msg * msg = NULL;
144 struct msg_hdr *hdr = NULL; 141 struct msg_hdr *hdr = NULL;
145 struct fd_pei pei; 142 struct fd_pei pei;
146 143
147 TRACE_ENTRY("%p", c); 144 TRACE_ENTRY("%p", arg);
148 145
146 /* Set the thread name */
147 {
148 char buf[48];
149 snprintf(buf, sizeof(buf), "Worker#%d[%s%s]", pw->id, IPPROTO_NAME(s->proto), s->secur?", Sec" : "");
150 fd_log_threadname ( buf );
151 }
152
153 /* Loop until canceled / error */
154 next_client:
155 LOG_A("Ready to process next incoming connection");
156
149 memset(&rcv_data, 0, sizeof(rcv_data)); 157 memset(&rcv_data, 0, sizeof(rcv_data));
150 158
151 CHECK_PARAMS_DO(c && c->conn && c->chain.head, goto fatal_error ); 159 /* Get the next connection */
152 160 CHECK_FCT_DO( fd_fifo_get( s->pending, &c ), { fatal = 1; goto cleanup; } );
153 161
154 s = c->chain.head->o;
155
156 /* Name the current thread */
157 fd_log_threadname ( fd_cnx_getid(c->conn) );
158
159 /* Handshake if we are a secure server port, or start clear otherwise */ 162 /* Handshake if we are a secure server port, or start clear otherwise */
160 if (s->secur) { 163 if (s->secur) {
161 int ret = fd_cnx_handshake(c->conn, GNUTLS_SERVER, (s->secur == 1) ? ALGO_HANDSHAKE_DEFAULT : ALGO_HANDSHAKE_3436, NULL, NULL); 164 LOG_D("Starting handshake with %s", fd_cnx_getid(c));
165
166 int ret = fd_cnx_handshake(c, GNUTLS_SERVER, (s->secur == 1) ? ALGO_HANDSHAKE_DEFAULT : ALGO_HANDSHAKE_3436, NULL, NULL);
162 if (ret != 0) { 167 if (ret != 0) {
163 char buf[1024]; 168 char buf[1024];
164 snprintf(buf, sizeof(buf), "TLS handshake failed for client '%s', connection aborted.", fd_cnx_getid(c->conn)); 169 snprintf(buf, sizeof(buf), "TLS handshake failed for connection '%s', connection closed.", fd_cnx_getid(c));
165 170
166 fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, NULL, buf, NULL); 171 fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, NULL, buf, NULL);
167 172
168 goto cleanup; 173 goto cleanup;
169 } 174 }
170 } else { 175 } else {
171 CHECK_FCT_DO( fd_cnx_start_clear(c->conn, 0), goto cleanup ); 176 CHECK_FCT_DO( fd_cnx_start_clear(c, 0), goto cleanup );
172 } 177 }
173 178
174 /* Set the timeout to receive the first message */ 179 /* Set the timeout to receive the first message */
175 CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &c->ts), goto fatal_error ); 180 CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &ts), { fatal = 1; goto cleanup; } );
176 c->ts.tv_sec += INCNX_TIMEOUT; 181 ts.tv_sec += INCNX_TIMEOUT;
177 182
178 /* Receive the first Diameter message on the connection -- cleanup in case of timeout */ 183 /* Receive the first Diameter message on the connection -- cleanup in case of timeout */
179 CHECK_FCT_DO( fd_cnx_receive(c->conn, &c->ts, &rcv_data.buffer, &rcv_data.length), 184 CHECK_FCT_DO( fd_cnx_receive(c, &ts, &rcv_data.buffer, &rcv_data.length),
180 { 185 {
181 char buf[1024]; 186 char buf[1024];
182 187
183 switch (__ret__) { 188 switch (__ret__) {
184 case ETIMEDOUT: 189 case ETIMEDOUT:
185 snprintf(buf, sizeof(buf), "Client '%s' did not send CER within %ds, connection aborted.", fd_cnx_getid(c->conn), INCNX_TIMEOUT); 190 snprintf(buf, sizeof(buf), "Client '%s' did not send CER within %ds, connection aborted.", fd_cnx_getid(c), INCNX_TIMEOUT);
186 fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, NULL, buf, NULL); 191 fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, NULL, buf, NULL);
187 break; 192 break;
188 193
189 case ENOTCONN: 194 case ENOTCONN:
190 snprintf(buf, sizeof(buf), "Connection from '%s' in error before CER was received.", fd_cnx_getid(c->conn)); 195 snprintf(buf, sizeof(buf), "Connection from '%s' in error before CER was received.", fd_cnx_getid(c));
191 fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, NULL, buf, NULL); 196 fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, NULL, buf, NULL);
192 break; 197 break;
193 198
194 default: 199 default:
195 snprintf(buf, sizeof(buf), "Connection from '%s': unspecified error, connection aborted.", fd_cnx_getid(c->conn)); 200 snprintf(buf, sizeof(buf), "Connection from '%s': unspecified error, connection aborted.", fd_cnx_getid(c));
196 fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, NULL, buf, NULL); 201 fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, NULL, buf, NULL);
197 } 202 }
198 goto cleanup; 203 goto cleanup;
199 } ); 204 } );
200 205
201 TRACE_DEBUG(FULL, "Received %zdb from new client '%s'", rcv_data.length, fd_cnx_getid(c->conn)); 206 TRACE_DEBUG(FULL, "Received %zdb from new client '%s'", rcv_data.length, fd_cnx_getid(c));
202 207
203 pmdl = fd_msg_pmdl_get_inbuf(rcv_data.buffer, rcv_data.length); 208 pmdl = fd_msg_pmdl_get_inbuf(rcv_data.buffer, rcv_data.length);
204 209
205 /* Try parsing this message */ 210 /* Try parsing this message */
206 CHECK_FCT_DO( fd_msg_parse_buffer( &rcv_data.buffer, rcv_data.length, &msg ), 211 CHECK_FCT_DO( fd_msg_parse_buffer( &rcv_data.buffer, rcv_data.length, &msg ),
209 goto cleanup; 214 goto cleanup;
210 } ); 215 } );
211 216
212 /* Log incoming message */ 217 /* Log incoming message */
213 fd_hook_associate(msg, pmdl); 218 fd_hook_associate(msg, pmdl);
214 fd_hook_call(HOOK_MESSAGE_RECEIVED, msg, NULL, fd_cnx_getid(c->conn), fd_msg_pmdl_get(msg)); 219 fd_hook_call(HOOK_MESSAGE_RECEIVED, msg, NULL, fd_cnx_getid(c), fd_msg_pmdl_get(msg));
215 220
216 /* We expect a CER, it must parse with our dictionary and rules */ 221 /* We expect a CER, it must parse with our dictionary and rules */
217 CHECK_FCT_DO( fd_msg_parse_rules( msg, fd_g_config->cnf_dict, &pei ), 222 CHECK_FCT_DO( fd_msg_parse_rules( msg, fd_g_config->cnf_dict, &pei ),
218 { /* Parsing failed -- trace details */ 223 { /* Parsing failed -- trace details */
219 char buf[1024]; 224 char buf[1024];
220 225
221 fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, msg, NULL, pei.pei_message ?: pei.pei_errcode, fd_msg_pmdl_get(msg)); 226 fd_hook_call(HOOK_MESSAGE_PARSING_ERROR, msg, NULL, pei.pei_message ?: pei.pei_errcode, fd_msg_pmdl_get(msg));
222 227
223 snprintf(buf, sizeof(buf), "Error parsing CER from '%s', connection aborted.", fd_cnx_getid(c->conn)); 228 snprintf(buf, sizeof(buf), "Error parsing CER from '%s', connection aborted.", fd_cnx_getid(c));
224 fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, NULL, buf, NULL); 229 fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, NULL, buf, NULL);
225 230
226 goto cleanup; 231 goto cleanup;
227 } ); 232 } );
228 233
229 /* Now check we received a CER */ 234 /* Now check we received a CER */
230 CHECK_FCT_DO( fd_msg_hdr ( msg, &hdr ), goto fatal_error ); 235 CHECK_FCT_DO( fd_msg_hdr ( msg, &hdr ), { fatal = 1; goto cleanup; } );
231 CHECK_PARAMS_DO( (hdr->msg_appl == 0) && (hdr->msg_flags & CMD_FLAG_REQUEST) && (hdr->msg_code == CC_CAPABILITIES_EXCHANGE), 236 CHECK_PARAMS_DO( (hdr->msg_appl == 0) && (hdr->msg_flags & CMD_FLAG_REQUEST) && (hdr->msg_code == CC_CAPABILITIES_EXCHANGE),
232 { /* Parsing failed -- trace details */ 237 { /* Parsing failed -- trace details */
233 char buf[1024]; 238 char buf[1024];
234 snprintf(buf, sizeof(buf), "Expected CER from '%s', received a different message, connection aborted.", fd_cnx_getid(c->conn)); 239 snprintf(buf, sizeof(buf), "Expected CER from '%s', received a different message, connection aborted.", fd_cnx_getid(c));
235 fd_hook_call(HOOK_PEER_CONNECT_FAILED, msg, NULL, buf, NULL); 240 fd_hook_call(HOOK_PEER_CONNECT_FAILED, msg, NULL, buf, NULL);
236 goto cleanup; 241 goto cleanup;
237 } ); 242 } );
238 243
239 /* Finally, pass the information to the peers module which will handle it next */ 244 /* Finally, pass the information to the peers module which will handle it in a separate thread */
240 pthread_cleanup_push((void *)fd_cnx_destroy, c->conn); 245 pthread_cleanup_push((void *)fd_cnx_destroy, c);
241 pthread_cleanup_push((void *)fd_msg_free, msg); 246 pthread_cleanup_push((void *)fd_msg_free, msg);
242 CHECK_FCT_DO( fd_peer_handle_newCER( &msg, &c->conn ), ); 247 CHECK_FCT_DO( fd_peer_handle_newCER( &msg, &c ), );
243 pthread_cleanup_pop(0); 248 pthread_cleanup_pop(0);
244 pthread_cleanup_pop(0); 249 pthread_cleanup_pop(0);
245 250
246 /* The end, we cleanup the client structure */
247 cleanup: 251 cleanup:
248 /* Unlink the client structure */
249 CHECK_POSIX_DO( pthread_mutex_lock(&s->clients_mtx), goto fatal_error );
250 fd_list_unlink( &c->chain );
251 CHECK_POSIX_DO( pthread_mutex_unlock(&s->clients_mtx), goto fatal_error );
252
253 /* Cleanup the parsed message if any */ 252 /* Cleanup the parsed message if any */
254 if (msg) { 253 if (msg) {
255 CHECK_FCT_DO( fd_msg_free(msg), /* continue */); 254 CHECK_FCT_DO( fd_msg_free(msg), /* continue */);
256 } 255 msg = NULL;
257 256 }
258 /* Destroy the connection object if present */ 257
259 if (c->conn) 258 /* Close the connection if needed */
260 fd_cnx_destroy(c->conn); 259 if (c != NULL) {
260 fd_cnx_destroy(c);
261 c = NULL;
262 }
261 263
262 /* Cleanup the received buffer if any */ 264 /* Cleanup the received buffer if any */
263 free(rcv_data.buffer); 265 free(rcv_data.buffer);
264 266
265 /* Detach the thread, cleanup the client structure */ 267
266 pthread_detach(pthread_self()); 268 if (!fatal)
267 free(c); 269 goto next_client;
270
271 LOG_E("Worker thread exiting.");
268 return NULL; 272 return NULL;
269 273 }
270 fatal_error: /* This has effect to terminate the daemon */
271 CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
272 return NULL;
273 }
274 274
275 /* The thread managing a server */ 275 /* The thread managing a server */
276 static void * serv_th(void * arg) 276 static void * serv_th(void * arg)
277 { 277 {
278 struct server *s = (struct server *)arg; 278 struct server *s = (struct server *)arg;
279 279
280 CHECK_PARAMS_DO(s, goto error); 280 CHECK_PARAMS_DO(s, goto error);
281 fd_log_threadname ( fd_cnx_getid(s->conn) ); 281 fd_log_threadname ( fd_cnx_getid(s->conn) );
282
282 set_status(s, RUNNING); 283 set_status(s, RUNNING);
283 284
284 /* Accept incoming connections */ 285 /* Accept incoming connections */
285 CHECK_FCT_DO( fd_cnx_serv_listen(s->conn), goto error ); 286 CHECK_FCT_DO( fd_cnx_serv_listen(s->conn), goto error );
286 287
287 do { 288 do {
288 struct client * c = NULL;
289 struct cnxctx * conn = NULL; 289 struct cnxctx * conn = NULL;
290 290
291 /* Wait for a new client or cancel */ 291 /* Wait for a new client or cancel */
292 CHECK_MALLOC_DO( conn = fd_cnx_serv_accept(s->conn), goto error ); 292 CHECK_MALLOC_DO( conn = fd_cnx_serv_accept(s->conn), break );
293 293
294 /* Create a client structure */ 294 /* Store this connection in the fifo for processing by the worker pool. Will block when the fifo is full */
295 CHECK_MALLOC_DO( c = malloc(sizeof(struct client)), goto error ); 295 pthread_cleanup_push((void *)fd_cnx_destroy, conn);
296 memset(c, 0, sizeof(struct client)); 296 CHECK_FCT_DO( fd_fifo_post( s->pending, &conn ), break );
297 fd_list_init(&c->chain, c); 297 pthread_cleanup_pop(0);
298 c->conn = conn;
299
300 /* Save the client in the list */
301 CHECK_POSIX_DO( pthread_mutex_lock( &s->clients_mtx ), goto error );
302 fd_list_insert_before(&s->clients, &c->chain);
303 CHECK_POSIX_DO( pthread_mutex_unlock( &s->clients_mtx ), goto error );
304
305 /* Start the client thread */
306 CHECK_POSIX_DO( pthread_create( &c->thr, NULL, client_sm, c ), goto error );
307 298
308 } while (1); 299 } while (1);
309
310 error: 300 error:
311 if (s) 301 if (s)
312 set_status(s, TERMINATED); 302 set_status(s, TERMINATED);
313 303
314 /* Send error signal to the core */ 304 /* Send error signal to the core */
315 LOG_F( "An error occurred in server module! Thread is terminating..."); 305 LOG_F( "An error occurred in server module! Thread is terminating...");
316 CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), ); 306 CHECK_FCT_DO(fd_core_shutdown(), );
317 307
318 return NULL; 308 return NULL;
319 } 309 }
320 310
321 311
322 /* Create a new server structure */ 312 /* Create a new server structure */
323 static struct server * new_serv( int proto, int secur ) 313 static struct server * new_serv( int proto, int secur )
324 { 314 {
325 struct server * new; 315 struct server * new;
316 int i;
326 317
327 /* New server structure */ 318 /* New server structure */
328 CHECK_MALLOC_DO( new = malloc(sizeof(struct server)), return NULL ); 319 CHECK_MALLOC_DO( new = malloc(sizeof(struct server)), return NULL );
329 320
330 memset(new, 0, sizeof(struct server)); 321 memset(new, 0, sizeof(struct server));
331 fd_list_init(&new->chain, new); 322 fd_list_init(&new->chain, new);
332 new->proto = proto; 323 new->proto = proto;
333 new->secur = secur; 324 new->secur = secur;
334 CHECK_POSIX_DO( pthread_mutex_init(&new->clients_mtx, NULL), return NULL ); 325
335 fd_list_init(&new->clients, new); 326 CHECK_FCT_DO( fd_fifo_new(&new->pending, 5), return NULL);
327 CHECK_MALLOC_DO( new->workers = calloc( fd_g_config->cnf_thr_srv, sizeof(struct pool_workers) ), return NULL );
328
329 for (i = 0; i < fd_g_config->cnf_thr_srv; i++) {
330 /* Create the pool */
331 new->workers[i].s = new;
332 new->workers[i].id = i;
333 CHECK_POSIX_DO( pthread_create( &new->workers[i].worker, NULL, client_worker, &new->workers[i]), return NULL );
334 }
336 335
337 return new; 336 return new;
338 } 337 }
339 338
340 /* Start all the servers */ 339 /* Start all the servers */
476 TRACE_DEBUG(INFO, "Shutting down server sockets..."); 475 TRACE_DEBUG(INFO, "Shutting down server sockets...");
477 476
478 /* Loop on all servers */ 477 /* Loop on all servers */
479 while (!FD_IS_LIST_EMPTY(&FD_SERVERS)) { 478 while (!FD_IS_LIST_EMPTY(&FD_SERVERS)) {
480 struct server * s = (struct server *)(FD_SERVERS.next); 479 struct server * s = (struct server *)(FD_SERVERS.next);
481 480 int i;
482 /* Lock client list now */ 481 struct cnxctx * c;
483 CHECK_FCT_DO( pthread_mutex_lock(&s->clients_mtx), /* continue anyway */);
484 482
485 /* cancel thread */ 483 /* cancel thread */
486 CHECK_FCT_DO( fd_thr_term(&s->thr), /* continue */); 484 CHECK_FCT_DO( fd_thr_term(&s->thr), /* continue */);
487 485
488 /* destroy server connection context */ 486 /* destroy server connection context */
489 fd_cnx_destroy(s->conn); 487 fd_cnx_destroy(s->conn);
490 488
491 /* cancel and destroy all clients */ 489 /* cancel and destroy all worker threads */
492 while (!FD_IS_LIST_EMPTY(&s->clients)) { 490 for (i = 0; i < fd_g_config->cnf_thr_srv; i++) {
493 struct client * c = (struct client *)(s->clients.next); 491 /* Destroy worker thread */
494 492 CHECK_FCT_DO( fd_thr_term(&s->workers[i].worker), /* continue */);
495 /* Destroy client's thread */ 493 }
496 CHECK_FCT_DO( fd_thr_term(&c->thr), /* continue */); 494 free(s->workers);
497 495
498 /* Destroy client's connection */ 496 /* Close any pending connection */
499 fd_cnx_destroy(c->conn); 497 while ( fd_fifo_tryget( s->pending, &c ) == 0 ) {
500 498 fd_cnx_destroy(c);
501 /* Unlink and free the client */ 499 }
502 fd_list_unlink(&c->chain); 500 CHECK_FCT_DO( fd_fifo_del(&s->pending), );
503 free(c);
504 }
505 /* Unlock & destroy */
506 CHECK_FCT_DO( pthread_mutex_unlock(&s->clients_mtx), /* continue anyway */);
507 CHECK_FCT_DO( pthread_mutex_destroy(&s->clients_mtx), /* continue */);
508 501
509 /* Now destroy the server object */ 502 /* Now destroy the server object */
510 fd_list_unlink(&s->chain); 503 fd_list_unlink(&s->chain);
511 free(s); 504 free(s);
512 } 505 }
"Welcome to our mercurial repository"