ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
server.c File Reference

Go to the source code of this file.

Functions

asciichat_error_t tcp_server_init (tcp_server_t *server, const tcp_server_config_t *config)
 
asciichat_error_t tcp_server_run (tcp_server_t *server)
 
void tcp_server_destroy (tcp_server_t *server)
 
void tcp_server_set_cleanup_callback (tcp_server_t *server, tcp_client_cleanup_fn cleanup_fn)
 
asciichat_error_t tcp_server_add_client (tcp_server_t *server, socket_t socket, void *client_data)
 
asciichat_error_t tcp_server_remove_client (tcp_server_t *server, socket_t socket)
 
asciichat_error_t tcp_server_get_client (tcp_server_t *server, socket_t socket, void **out_data)
 
void tcp_server_foreach_client (tcp_server_t *server, tcp_client_foreach_fn callback, void *user_arg)
 
size_t tcp_server_get_client_count (tcp_server_t *server)
 
const char * tcp_client_context_get_ip (const tcp_client_context_t *ctx, char *buf, size_t len)
 
int tcp_client_context_get_port (const tcp_client_context_t *ctx)
 
void tcp_server_reject_client (socket_t socket, const char *reason)
 
asciichat_error_t tcp_server_spawn_thread (tcp_server_t *server, socket_t client_socket, void *(*thread_func)(void *), void *thread_arg, int stop_id, const char *thread_name)
 
asciichat_error_t tcp_server_stop_client_threads (tcp_server_t *server, socket_t client_socket)
 
asciichat_error_t tcp_server_get_thread_count (tcp_server_t *server, socket_t client_socket, size_t *count)
 

Function Documentation

◆ tcp_client_context_get_ip()

const char * tcp_client_context_get_ip ( const tcp_client_context_t *  ctx,
char *  buf,
size_t  len 
)

Definition at line 476 of file lib/network/tcp/server.c.

476 {
477 if (!ctx) {
478 SET_ERRNO(ERROR_INVALID_PARAM, "ctx is NULL");
479 return NULL;
480 }
481 if (!buf) {
482 SET_ERRNO(ERROR_INVALID_PARAM, "buf is NULL");
483 return NULL;
484 }
485 if (len == 0) {
486 SET_ERRNO(ERROR_INVALID_PARAM, "len is 0");
487 return NULL;
488 }
489
490 // Determine address family
491 int addr_family = (ctx->addr.ss_family == AF_INET) ? AF_INET : AF_INET6;
492
493 // Format IP address using existing utility
494 if (format_ip_address(addr_family, (struct sockaddr *)&ctx->addr, buf, len) != 0) {
495 return NULL;
496 }
497
498 return buf;
499}
asciichat_error_t format_ip_address(int family, const struct sockaddr *addr, char *output, size_t output_size)
Definition ip.c:196

References format_ip_address().

Referenced by acds_client_handler().

◆ tcp_client_context_get_port()

int tcp_client_context_get_port ( const tcp_client_context_t *  ctx)

Definition at line 501 of file lib/network/tcp/server.c.

501 {
502 if (!ctx) {
503 SET_ERRNO(ERROR_INVALID_PARAM, "ctx is NULL");
504 return -1;
505 }
506
507 // Extract port based on address family
508 if (ctx->addr.ss_family == AF_INET) {
509 struct sockaddr_in *addr_in = (struct sockaddr_in *)&ctx->addr;
510 return ntohs(addr_in->sin_port);
511 } else if (ctx->addr.ss_family == AF_INET6) {
512 struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)&ctx->addr;
513 return ntohs(addr_in6->sin6_port);
514 }
515
516 SET_ERRNO(ERROR_INVALID_STATE, "Unknown address family: %d", ctx->addr.ss_family);
517 return -1;
518}

◆ tcp_server_add_client()

asciichat_error_t tcp_server_add_client ( tcp_server_t *  server,
socket_t  socket,
void *  client_data 
)

Definition at line 350 of file lib/network/tcp/server.c.

350 {
351 if (!server) {
352 return SET_ERRNO(ERROR_INVALID_PARAM, "server is NULL");
353 }
354
355 if (socket == INVALID_SOCKET_VALUE) {
356 return SET_ERRNO(ERROR_INVALID_PARAM, "socket is invalid");
357 }
358
359 // Allocate new entry
360 tcp_client_entry_t *entry = SAFE_MALLOC(sizeof(tcp_client_entry_t), tcp_client_entry_t *);
361 if (!entry) {
362 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate client entry");
363 }
364
365 entry->socket = socket;
366 entry->client_data = client_data;
367
368 // Create thread pool for this client
369 char pool_name[64];
370 SAFE_SNPRINTF(pool_name, sizeof(pool_name), "client_%d", socket);
371 entry->threads = thread_pool_create(pool_name);
372 if (!entry->threads) {
373 SAFE_FREE(entry);
374 return SET_ERRNO(ERROR_MEMORY, "Failed to create thread pool for client");
375 }
376
377 // Add to hash table (thread-safe)
378 mutex_lock(&server->clients_mutex);
379 HASH_ADD(hh, server->clients, socket, sizeof(socket_t), entry);
380 mutex_unlock(&server->clients_mutex);
381
382 log_debug("Added client socket=%d to registry", socket);
383 return ASCIICHAT_OK;
384}
int socket_t
thread_pool_t * thread_pool_create(const char *pool_name)
Definition thread_pool.c:17

References thread_pool_create().

Referenced by acds_client_handler(), and add_client().

◆ tcp_server_destroy()

void tcp_server_destroy ( tcp_server_t *  server)

Definition at line 286 of file lib/network/tcp/server.c.

286 {
287 if (!server) {
288 return;
289 }
290
291 log_debug("Shutting down TCP server...");
292
293 // Signal server to stop
294 atomic_store(&server->running, false);
295
296 // Close listen sockets
297 if (server->listen_socket != INVALID_SOCKET_VALUE) {
298 log_debug("Closing IPv4 listen socket");
299 socket_close(server->listen_socket);
300 server->listen_socket = INVALID_SOCKET_VALUE;
301 }
302
303 if (server->listen_socket6 != INVALID_SOCKET_VALUE) {
304 log_debug("Closing IPv6 listen socket");
305 socket_close(server->listen_socket6);
306 server->listen_socket6 = INVALID_SOCKET_VALUE;
307 }
308
309 // Clean up client registry
310 mutex_lock(&server->clients_mutex);
311
312 tcp_client_entry_t *entry = NULL, *tmp = NULL;
313 HASH_ITER(hh, server->clients, entry, tmp) {
314 // Call cleanup callback if set
315 if (server->cleanup_fn && entry->client_data) {
316 server->cleanup_fn(entry->client_data);
317 }
318
319 // Destroy thread pool (stops all threads and frees resources)
320 if (entry->threads) {
321 thread_pool_destroy(entry->threads);
322 entry->threads = NULL;
323 }
324
325 HASH_DEL(server->clients, entry);
326 SAFE_FREE(entry);
327 }
328 server->clients = NULL;
329
330 mutex_unlock(&server->clients_mutex);
331 mutex_destroy(&server->clients_mutex);
332
333 // Note: This function does NOT wait for client threads to exit
334 // Caller is responsible for thread lifecycle management
335
336 log_debug("TCP server shutdown complete");
337}
void thread_pool_destroy(thread_pool_t *pool)
Definition thread_pool.c:48
int mutex_destroy(mutex_t *mutex)
Definition threading.c:21

References mutex_destroy(), and thread_pool_destroy().

Referenced by acds_server_init(), acds_server_shutdown(), and server_main().

◆ tcp_server_foreach_client()

void tcp_server_foreach_client ( tcp_server_t *  server,
tcp_client_foreach_fn  callback,
void *  user_arg 
)

Definition at line 445 of file lib/network/tcp/server.c.

445 {
446 if (!server || !callback) {
447 return;
448 }
449
450 mutex_lock(&server->clients_mutex);
451
452 tcp_client_entry_t *entry, *tmp;
453 HASH_ITER(hh, server->clients, entry, tmp) {
454 callback(entry->socket, entry->client_data, user_arg);
455 }
456
457 mutex_unlock(&server->clients_mutex);
458}

Referenced by signaling_broadcast(), signaling_relay_ice(), and signaling_relay_sdp().

◆ tcp_server_get_client()

asciichat_error_t tcp_server_get_client ( tcp_server_t *  server,
socket_t  socket,
void **  out_data 
)

Definition at line 423 of file lib/network/tcp/server.c.

423 {
424 if (!server || !out_data) {
425 return SET_ERRNO(ERROR_INVALID_PARAM, "server or out_data is NULL");
426 }
427
428 mutex_lock(&server->clients_mutex);
429
430 tcp_client_entry_t *entry = NULL;
431 HASH_FIND(hh, server->clients, &socket, sizeof(socket_t), entry);
432
433 if (!entry) {
434 *out_data = NULL;
435 mutex_unlock(&server->clients_mutex);
436 return SET_ERRNO(ERROR_INVALID_STATE, "Client socket=%d not in registry", socket);
437 }
438
439 *out_data = entry->client_data;
440 mutex_unlock(&server->clients_mutex);
441
442 return ASCIICHAT_OK;
443}

◆ tcp_server_get_client_count()

size_t tcp_server_get_client_count ( tcp_server_t *  server)

Definition at line 460 of file lib/network/tcp/server.c.

460 {
461 if (!server) {
462 return 0;
463 }
464
465 mutex_lock(&server->clients_mutex);
466 size_t count = HASH_COUNT(server->clients);
467 mutex_unlock(&server->clients_mutex);
468
469 return count;
470}

Referenced by acds_client_handler(), acds_server_shutdown(), discovery_status_gather(), and server_status_gather().

◆ tcp_server_get_thread_count()

asciichat_error_t tcp_server_get_thread_count ( tcp_server_t *  server,
socket_t  client_socket,
size_t *  count 
)

Definition at line 621 of file lib/network/tcp/server.c.

621 {
622 if (!server || !count) {
623 return SET_ERRNO(ERROR_INVALID_PARAM, "server or count is NULL");
624 }
625
626 if (client_socket == INVALID_SOCKET_VALUE) {
627 return SET_ERRNO(ERROR_INVALID_PARAM, "client_socket is invalid");
628 }
629
630 *count = 0;
631
632 // Find client entry
633 mutex_lock(&server->clients_mutex);
634 tcp_client_entry_t *entry = NULL;
635 HASH_FIND(hh, server->clients, &client_socket, sizeof(socket_t), entry);
636
637 if (!entry) {
638 mutex_unlock(&server->clients_mutex);
639 return SET_ERRNO(ERROR_NOT_FOUND, "Client socket=%d not in registry", client_socket);
640 }
641
642 // Get thread count from pool
643 if (entry->threads) {
644 *count = thread_pool_get_count(entry->threads);
645 }
646
647 mutex_unlock(&server->clients_mutex);
648
649 return ASCIICHAT_OK;
650}
size_t thread_pool_get_count(const thread_pool_t *pool)

References thread_pool_get_count().

◆ tcp_server_init()

asciichat_error_t tcp_server_init ( tcp_server_t *  server,
const tcp_server_config_t *  config 
)

Definition at line 103 of file lib/network/tcp/server.c.

103 {
104 if (!server || !config) {
105 return SET_ERRNO(ERROR_INVALID_PARAM, "server or config is NULL");
106 }
107
108 // Note: client_handler is optional - some users may use tcp_server just for socket setup
109 // and implement their own accept loop (like ascii-chat server with its cleanup logic)
110
111 // Initialize server state
112 memset(server, 0, sizeof(*server));
113 server->listen_socket = INVALID_SOCKET_VALUE;
114 server->listen_socket6 = INVALID_SOCKET_VALUE;
115 atomic_store(&server->running, true);
116 server->config = *config; // Copy config
117
118 // Initialize client registry
119 server->clients = NULL; // uthash starts with NULL
120 server->cleanup_fn = NULL;
121 if (mutex_init(&server->clients_mutex) != 0) {
122 return SET_ERRNO(ERROR_THREAD, "Failed to initialize clients mutex");
123 }
124
125 // Determine which IP versions to bind
126 bool should_bind_ipv4 = config->bind_ipv4;
127 bool should_bind_ipv6 = config->bind_ipv6;
128
129 // Bind IPv4 socket if requested
130 if (should_bind_ipv4) {
131 const char *ipv4_addr = (config->ipv4_address && config->ipv4_address[0] != '\0') ? config->ipv4_address : NULL;
132 server->listen_socket = bind_and_listen(ipv4_addr, AF_INET, config->port);
133
134 if (server->listen_socket == INVALID_SOCKET_VALUE) {
135 log_warn("Failed to bind IPv4 socket");
136 }
137 }
138
139 // Bind IPv6 socket if requested
140 if (should_bind_ipv6) {
141 const char *ipv6_addr = (config->ipv6_address && config->ipv6_address[0] != '\0') ? config->ipv6_address : NULL;
142 server->listen_socket6 = bind_and_listen(ipv6_addr, AF_INET6, config->port);
143
144 if (server->listen_socket6 == INVALID_SOCKET_VALUE) {
145 log_warn("Failed to bind IPv6 socket");
146 }
147 }
148
149 // Ensure at least one socket bound successfully
150 if (server->listen_socket == INVALID_SOCKET_VALUE && server->listen_socket6 == INVALID_SOCKET_VALUE) {
151 return SET_ERRNO(ERROR_NETWORK_BIND, "Failed to bind any sockets (IPv4 and IPv6 both failed)");
152 }
153
154 return ASCIICHAT_OK;
155}
int mutex_init(mutex_t *mutex)
Definition threading.c:16

References mutex_init().

Referenced by acds_server_init(), and server_main().

◆ tcp_server_reject_client()

void tcp_server_reject_client ( socket_t  socket,
const char *  reason 
)

Definition at line 520 of file lib/network/tcp/server.c.

520 {
521 if (socket == INVALID_SOCKET_VALUE) {
522 SET_ERRNO(ERROR_INVALID_PARAM, "socket is INVALID_SOCKET_VALUE");
523 return;
524 }
525
526 log_warn("Rejecting client connection: %s", reason ? reason : "unknown reason");
527 socket_close(socket);
528}

Referenced by acds_client_handler().

◆ tcp_server_remove_client()

asciichat_error_t tcp_server_remove_client ( tcp_server_t *  server,
socket_t  socket 
)

Definition at line 386 of file lib/network/tcp/server.c.

386 {
387 if (!server) {
388 return SET_ERRNO(ERROR_INVALID_PARAM, "server is NULL");
389 }
390
391 mutex_lock(&server->clients_mutex);
392
393 tcp_client_entry_t *entry = NULL;
394 HASH_FIND(hh, server->clients, &socket, sizeof(socket_t), entry);
395
396 if (!entry) {
397 mutex_unlock(&server->clients_mutex);
398 // Already removed (e.g., during shutdown) - this is fine
399 log_debug("Client socket=%d already removed from registry", socket);
400 return ASCIICHAT_OK;
401 }
402
403 // Call cleanup callback if set
404 if (server->cleanup_fn && entry->client_data) {
405 server->cleanup_fn(entry->client_data);
406 }
407
408 // Destroy thread pool (stops all threads and frees resources)
409 if (entry->threads) {
410 thread_pool_destroy(entry->threads);
411 entry->threads = NULL;
412 }
413
414 HASH_DEL(server->clients, entry);
415 SAFE_FREE(entry);
416
417 mutex_unlock(&server->clients_mutex);
418
419 log_debug("Removed client socket=%d from registry", socket);
420 return ASCIICHAT_OK;
421}

References thread_pool_destroy().

Referenced by acds_client_handler().

◆ tcp_server_run()

asciichat_error_t tcp_server_run ( tcp_server_t *  server)

Definition at line 157 of file lib/network/tcp/server.c.

157 {
158 if (!server) {
159 return SET_ERRNO(ERROR_INVALID_PARAM, "server is NULL");
160 }
161
162 if (!server->config.client_handler) {
163 return SET_ERRNO(ERROR_INVALID_PARAM,
164 "client_handler is required for tcp_server_run() - use custom accept loop if handler is NULL");
165 }
166
167 log_debug("TCP server starting accept loop...");
168
169 while (atomic_load(&server->running)) {
170 // Build fd_set for select()
171 fd_set read_fds;
172 socket_fd_zero(&read_fds);
173 socket_t max_fd = 0;
174
175 // Add IPv4 socket if available
176 if (server->listen_socket != INVALID_SOCKET_VALUE) {
177 socket_fd_set(server->listen_socket, &read_fds);
178 max_fd = server->listen_socket > max_fd ? server->listen_socket : max_fd;
179 }
180
181 // Add IPv6 socket if available
182 if (server->listen_socket6 != INVALID_SOCKET_VALUE) {
183 socket_fd_set(server->listen_socket6, &read_fds);
184 max_fd = server->listen_socket6 > max_fd ? server->listen_socket6 : max_fd;
185 }
186
187 // Use timeout from config (defaults to 1 second if not set)
188 // Convert double seconds to tv_sec and tv_usec
189 double timeout_sec_double = server->config.accept_timeout_sec > 0 ? server->config.accept_timeout_sec : 1.0;
190 time_t timeout_sec = (time_t)timeout_sec_double;
191 long timeout_usec = (long)((timeout_sec_double - timeout_sec) * (double)US_PER_SEC_INT);
192 struct timeval timeout = {.tv_sec = timeout_sec, .tv_usec = timeout_usec};
193
194 int select_result = socket_select((int)(max_fd + 1), &read_fds, NULL, NULL, &timeout);
195
196 if (select_result < 0) {
197 // Check if interrupted by signal (expected during shutdown)
198 int err = socket_get_last_error();
199 if (err == EINTR) {
200 // Signal interrupt (e.g., SIGTERM, SIGINT) - check running flag and continue
201 log_debug("select() interrupted by signal");
202 continue;
203 }
204 // Actual error - socket_get_error_string() returns last socket error
205 log_error("select() failed in accept loop: %s (errno=%d)", socket_get_error_string(), err);
206 continue;
207 }
208
209 if (select_result == 0) {
210 // Timeout - invoke status update callback if configured
211 if (server->config.status_update_fn) {
212 server->config.status_update_fn(server->config.status_update_data);
213 }
214 continue;
215 }
216
217 // Check which socket has incoming connection
218 socket_t ready_socket = INVALID_SOCKET_VALUE;
219 if (server->listen_socket != INVALID_SOCKET_VALUE && socket_fd_isset(server->listen_socket, &read_fds)) {
220 ready_socket = server->listen_socket;
221 } else if (server->listen_socket6 != INVALID_SOCKET_VALUE && socket_fd_isset(server->listen_socket6, &read_fds)) {
222 ready_socket = server->listen_socket6;
223 }
224
225 if (ready_socket == INVALID_SOCKET_VALUE) {
226 // Spurious wakeup
227 continue;
228 }
229
230 // Accept connection
231 struct sockaddr_storage client_addr;
232 socklen_t client_addr_len = sizeof(client_addr);
233 socket_t client_socket = accept(ready_socket, (struct sockaddr *)&client_addr, &client_addr_len);
234
235 if (client_socket == INVALID_SOCKET_VALUE) {
236 log_warn("Failed to accept connection");
237 continue;
238 }
239
240 // Format client IP for logging
241 char client_ip[INET6_ADDRSTRLEN];
242 int addr_family = (client_addr.ss_family == AF_INET) ? AF_INET : AF_INET6;
243 if (format_ip_address(addr_family, (struct sockaddr *)&client_addr, client_ip, sizeof(client_ip)) != ASCIICHAT_OK) {
244 SAFE_STRNCPY(client_ip, "(unknown)", sizeof(client_ip));
245 }
246
247 log_debug("Accepted connection from %s", client_ip);
248
249 // Allocate client context
250 tcp_client_context_t *ctx = SAFE_MALLOC(sizeof(tcp_client_context_t), tcp_client_context_t *);
251 if (!ctx) {
252 log_error("Failed to allocate client context");
253 socket_close(client_socket);
254 continue;
255 }
256
257 ctx->client_socket = client_socket;
258 ctx->addr = client_addr;
259 ctx->addr_len = client_addr_len;
260 ctx->user_data = server->config.user_data;
261
262 // Spawn client handler thread
263 // Handler is responsible for:
264 // 1. Allocating client_data
265 // 2. Calling tcp_server_add_client() to register
266 // 3. Spawning additional worker threads via tcp_server_spawn_thread() if needed
267 // 4. Processing client requests
268 // 5. Calling tcp_server_remove_client() on disconnect
269 // 6. Closing socket and freeing ctx
270 asciichat_thread_t thread;
271 if (asciichat_thread_create(&thread, server->config.client_handler, ctx) != 0) {
272 log_error("Failed to create client handler thread for %s", client_ip);
273 SAFE_FREE(ctx);
274 socket_close(client_socket);
275 continue;
276 }
277
278 // Thread is detached (handler is responsible for cleanup)
279 (void)thread; // Suppress unused warning
280 }
281
282 log_debug("TCP server accept loop exited");
283 return ASCIICHAT_OK;
284}
int asciichat_thread_create(asciichat_thread_t *thread, void *(*start_routine)(void *), void *arg)
Definition threading.c:42

References asciichat_thread_create(), and format_ip_address().

Referenced by acds_server_run(), and server_main().

◆ tcp_server_set_cleanup_callback()

void tcp_server_set_cleanup_callback ( tcp_server_t *  server,
tcp_client_cleanup_fn  cleanup_fn 
)

Definition at line 343 of file lib/network/tcp/server.c.

343 {
344 if (!server) {
345 return;
346 }
347 server->cleanup_fn = cleanup_fn;
348}

◆ tcp_server_spawn_thread()

asciichat_error_t tcp_server_spawn_thread ( tcp_server_t *  server,
socket_t  client_socket,
void *(*)(void *)  thread_func,
void *  thread_arg,
int  stop_id,
const char *  thread_name 
)

Definition at line 534 of file lib/network/tcp/server.c.

535 {
536 if (!server || !thread_func) {
537 return SET_ERRNO(ERROR_INVALID_PARAM, "server or thread_func is NULL");
538 }
539
540 // For WebRTC clients (no socket): create thread directly without TCP server thread pool tracking
541 // This allows render threads to work for both TCP and WebRTC clients
542 if (client_socket == INVALID_SOCKET_VALUE) {
543 // Extract thread handle from thread_arg if it's a client_info_t
544 // We need to store the thread handle somewhere, but for now just create the thread
545 // The caller must manage the thread handle directly for WebRTC clients
546 log_debug("Spawning standalone thread '%s' (no socket, WebRTC client)", thread_name ? thread_name : "unnamed");
547
548 // For WebRTC clients, we can't use thread_pool since there's no socket entry
549 // Instead, create the thread directly - the caller must handle the thread
550 // This is a hack to allow reusing tcp_server_spawn_thread for WebRTC
551 // In the future, consider a unified thread management system
552 asciichat_thread_t temp_thread;
553 asciichat_error_t result = asciichat_thread_create(&temp_thread, thread_func, thread_arg);
554 if (result != ASCIICHAT_OK) {
555 return result;
556 }
557
558 // Note: temp_thread handle is lost here, but the thread is running
559 // The caller must manage thread lifecycle for WebRTC clients differently
560 (void)temp_thread; // Suppress unused warning
561 return ASCIICHAT_OK;
562 }
563
564 // Find client entry
565 mutex_lock(&server->clients_mutex);
566 tcp_client_entry_t *entry = NULL;
567 HASH_FIND(hh, server->clients, &client_socket, sizeof(socket_t), entry);
568
569 if (!entry) {
570 mutex_unlock(&server->clients_mutex);
571 return SET_ERRNO(ERROR_NOT_FOUND, "Client socket=%d not in registry", client_socket);
572 }
573
574 // Spawn thread in client's thread pool
575 asciichat_error_t result = thread_pool_spawn(entry->threads, thread_func, thread_arg, stop_id, thread_name);
576
577 mutex_unlock(&server->clients_mutex);
578
579 if (result != ASCIICHAT_OK) {
580 return result;
581 }
582
583 size_t thread_count = thread_pool_get_count(entry->threads);
584 log_debug("Spawned thread '%s' (stop_id=%d) for client socket=%d (total_threads=%zu)",
585 thread_name ? thread_name : "unnamed", stop_id, client_socket, thread_count);
586
587 return ASCIICHAT_OK;
588}
asciichat_error_t thread_pool_spawn(thread_pool_t *pool, void *(*thread_func)(void *), void *thread_arg, int stop_id, const char *thread_name)
Definition thread_pool.c:70

References asciichat_thread_create(), thread_pool_get_count(), and thread_pool_spawn().

Referenced by create_client_render_threads().

◆ tcp_server_stop_client_threads()

asciichat_error_t tcp_server_stop_client_threads ( tcp_server_t *  server,
socket_t  client_socket 
)

Definition at line 590 of file lib/network/tcp/server.c.

590 {
591 if (!server) {
592 return SET_ERRNO(ERROR_INVALID_PARAM, "server is NULL");
593 }
594
595 if (client_socket == INVALID_SOCKET_VALUE) {
596 return SET_ERRNO(ERROR_INVALID_PARAM, "client_socket is invalid");
597 }
598
599 // Find client entry
600 mutex_lock(&server->clients_mutex);
601 tcp_client_entry_t *entry = NULL;
602 HASH_FIND(hh, server->clients, &client_socket, sizeof(socket_t), entry);
603
604 if (!entry) {
605 mutex_unlock(&server->clients_mutex);
606 return SET_ERRNO(ERROR_NOT_FOUND, "Client socket=%d not in registry", client_socket);
607 }
608
609 // Stop all threads in client's thread pool (in stop_id order)
610 asciichat_error_t result = ASCIICHAT_OK;
611 if (entry->threads) {
612 result = thread_pool_stop_all(entry->threads);
613 }
614
615 mutex_unlock(&server->clients_mutex);
616
617 log_debug("All threads stopped for client socket=%d", client_socket);
618 return result;
619}
asciichat_error_t thread_pool_stop_all(thread_pool_t *pool)

References thread_pool_stop_all().

Referenced by remove_client().