1 /*
2 * coserver.c - basic internal coserver routines
3 *
4 * Copyright (C) 2011-2013 Thien-Thi Nguyen
5 * Copyright (C) 2000, 2001, 2002, 2003 Stefan Jahn <stefan@lkcc.org>
6 *
7 * This is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3, or (at your option)
10 * any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this package. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22
23 #include "timidity.h"
24 #include "unused.h"
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <time.h>
29 #include <sys/types.h>
30 #include <errno.h>
31 #if HAVE_UNISTD_H
32 # include <unistd.h>
33 #endif
34 #include <signal.h>
35 #ifndef __MINGW32__
36 # if HAVE_WAIT_H
37 # include <wait.h>
38 # endif
39 # if HAVE_SYS_WAIT_H
40 # include <sys/wait.h>
41 # endif
42 #endif
43 #include "networking-headers.h"
44 #include "libserveez/alloc.h"
45 #include "libserveez/util.h"
46 #include "libserveez/core.h"
47 #include "libserveez/hash.h"
48 #include "libserveez/array.h"
49 #include "libserveez/pipe-socket.h"
50 #include "libserveez/server-core.h"
51 #include "libserveez/coserver/coserver.h"
52
53 /* coserver-TODO: include header here */
54 #include "dns.h"
55 #include "reverse-dns.h"
56 #include "ident.h"
57
58 #ifdef __MINGW32__
59 /* define for the thread priority in Win32 */
60 #define COSERVER_THREAD_PRIORITY THREAD_PRIORITY_IDLE
61 #endif /* not __MINGW32__ */
62
63 #define COSERVER_PACKET_BOUNDARY '\n' /* packet boundary */
64 #define COSERVER_ID_BOUNDARY ':' /* id boundary */
65
66 /*
67 * This structure contains the type id and the callback
68 * pointer of the internal coserver routines where CALLBACK is
69 * the actual (blocking) processing routine.
70 */
71 typedef struct
72 {
73 int type; /* coserver type id */
74 char *name; /* name of the internal coserver */
75 char * (* callback) (char *); /* coserver callback */
76 int instances; /* the amount of coserver instances */
77 void (* init) (void); /* coserver initialization routine */
78 long last_start; /* time stamp of the last instance ‘fork’ */
79 }
80 svz_coservertype_t;
81
82 /*
83 * Both of these variables are for storing the given callbacks which get
84 * called when the coservers delivered some result.
85 */
86 static unsigned callback_id = 1;
87 static svz_hash_t *callbacks = NULL;
88
89 /*
90 * Internal coserver instances.
91 */
92 static svz_array_t *coservers = NULL;
93
94 /*
95 * Hash table to map a socket to a coserver.
96 */
97 static svz_hash_t *friendly;
98
99 /*
100 * Invoke a @var{request} for one of the running internal coservers
101 * with type @var{type}. @var{handle_result} and @var{arg} specify what
102 * should happen if the coserver delivers a result.
103 */
104 static void
send_request(int type,const char * request,svz_coserver_handle_result_t handle_result,void * closure)105 send_request (int type, const char *request,
106 svz_coserver_handle_result_t handle_result,
107 void *closure)
108 {
109 size_t n;
110 svz_coserver_t *coserver, *current;
111 svz_coserver_callback_t *cb;
112
113 /*
114 * Go through all coservers and find out which coserver
115 * type TYPE is the least busiest.
116 */
117 coserver = NULL;
118 svz_array_foreach (coservers, current, n)
119 {
120 if (current->type == type)
121 {
122 if (coserver == NULL
123 || current->busy <= coserver->busy)
124 coserver = current;
125 }
126 }
127
128 /* found an appropriate coserver */
129 if (coserver)
130 {
131 /*
132 * Create new callback hash entry for this coserver request and
133 * put it into the global coserver callback hash.
134 */
135 cb = svz_malloc (sizeof (svz_coserver_callback_t));
136 cb->handle_result = handle_result;
137 cb->closure = closure;
138 svz_hash_put (callbacks,
139 svz_itoa (callback_id), cb);
140
141 coserver->busy++;
142 #ifdef __MINGW32__
143 EnterCriticalSection (&coserver->sync);
144 #endif /* __MINGW32__ */
145 if (svz_sock_printf (coserver->sock, "%u:%s\n",
146 callback_id, request))
147 {
148 svz_sock_schedule_for_shutdown (coserver->sock);
149 }
150 callback_id++;
151 #ifdef __MINGW32__
152 LeaveCriticalSection (&coserver->sync);
153 coserver_activate (coserver->type);
154 #endif /* __MINGW32__ */
155 }
156 }
157
158 svz_sock_iv_t *
svz_make_sock_iv(svz_socket_t * sock)159 svz_make_sock_iv (svz_socket_t *sock)
160 {
161 svz_sock_iv_t *rv = svz_malloc (sizeof (svz_sock_iv_t));
162
163 rv->id = sock->id;
164 rv->version = sock->version;
165 return rv;
166 }
167
168 /* coserver-TODO:
169 place an appropiate wrapper function here */
170
171 /**
172 * Enqueue a request for the reverse DNS coserver
173 * to resolve address @var{addr},
174 * arranging for callback @var{cb} to be called with two args:
175 * the hostname (a string) and the opaque data @var{closure}.
176 */
177 void
svz_coserver_rdns_invoke(svz_address_t * addr,svz_coserver_handle_result_t cb,void * closure)178 svz_coserver_rdns_invoke (svz_address_t *addr,
179 svz_coserver_handle_result_t cb,
180 void *closure)
181 {
182 char buf[64];
183
184 STILL_NO_V6_DAMMIT (addr);
185 send_request (SVZ_COSERVER_REVERSE_DNS,
186 SVZ_PP_ADDR (buf, addr),
187 cb, closure);
188 }
189
190 /**
191 * Enqueue a request for the DNS coserver to resolve @var{host},
192 * arranging for callback @var{cb} to be called with two args:
193 * the ip address in dots-and-numbers notation and the opaque
194 * data @var{closure}.
195 */
196 void
svz_coserver_dns_invoke(char * host,svz_coserver_handle_result_t cb,void * closure)197 svz_coserver_dns_invoke (char *host,
198 svz_coserver_handle_result_t cb,
199 void *closure)
200 {
201 send_request (SVZ_COSERVER_DNS, host, cb, closure);
202 }
203
204 /**
205 * Enqueue a request for the ident coserver to resolve the client
206 * identity at @var{sock}, arranging for callback @var{cb} to be called
207 * with two args: the identity (string) and the opaque data @var{closure}.
208 */
209 void
svz_coserver_ident_invoke(svz_socket_t * sock,svz_coserver_handle_result_t cb,void * closure)210 svz_coserver_ident_invoke (svz_socket_t *sock,
211 svz_coserver_handle_result_t cb,
212 void *closure)
213 {
214 char addrbuf[64];
215 char buffer[COSERVER_BUFSIZE];
216
217 snprintf (buffer, COSERVER_BUFSIZE, "%s:%u",
218 SVZ_PP_ADDR_PORT (addrbuf, sock->remote_addr, sock->remote_port),
219 ntohs (sock->local_port));
220 send_request (SVZ_COSERVER_IDENT, buffer, cb, closure);
221 }
222
223 /*
224 * This static array contains the coserver structure for each type of
225 * internal coserver the core library provides.
226 */
227 static svz_coservertype_t coservertypes[] =
228 {
229 /* coserver-TODO:
230 place coserver callbacks and identification here */
231
232 { SVZ_COSERVER_REVERSE_DNS, "reverse dns",
233 reverse_dns_handle_request, 1, reverse_dns_init, 0 },
234
235 { SVZ_COSERVER_IDENT, "ident",
236 ident_handle_request, 1, NULL, 0},
237
238 { SVZ_COSERVER_DNS, "dns",
239 dns_handle_request, 1, NULL, 0 }
240 };
241
242 /**
243 * Call @var{func} for each coserver, passing additionally the second arg
244 * @var{closure}. If @var{func} returns a negative value, return immediately
245 * with that value (breaking out of the loop), otherwise, return 0.
246 */
247 int
svz_foreach_coserver(svz_coserver_do_t * func,void * closure)248 svz_foreach_coserver (svz_coserver_do_t *func, void *closure)
249 {
250 size_t n;
251 int rv;
252 const svz_coserver_t *coserver;
253
254 svz_array_foreach (coservers, coserver, n)
255 {
256 if (0 > (rv = func (coserver, closure)))
257 return rv;
258 }
259 return 0;
260 }
261
262 /*
263 * This routine gets the coserver hash id from a given response and
264 * cuts it from the given response buffer.
265 */
266 static unsigned
get_id(char * response)267 get_id (char *response)
268 {
269 char *p = response;
270 unsigned id = 0;
271
272 while (*p >= '0' && *p <= '9')
273 {
274 id *= 10;
275 id += *p - '0';
276 p++;
277 }
278 if (*p != COSERVER_ID_BOUNDARY)
279 {
280 svz_log (SVZ_LOG_WARNING,
281 "coserver: invalid protocol character (0x%02x)\n", *p);
282 return 0;
283 }
284 p++;
285
286 while (*p != COSERVER_PACKET_BOUNDARY)
287 {
288 *response++ = *p++;
289 }
290 *response = '\0';
291 return id;
292 }
293
294 /*
295 * This function adds a given coserver hash id to the response.
296 */
297 static void
put_id(unsigned id,char * response)298 put_id (unsigned id, char *response)
299 {
300 char buffer[COSERVER_BUFSIZE];
301
302 snprintf (buffer, COSERVER_BUFSIZE, "%u:%s\n", id, response);
303 strcpy (response, buffer);
304 }
305
306 /*************************************************************************/
307 /* This is part of the coserver process / thread. */
308 /*************************************************************************/
309
310 /*
311 * Win32:
312 * ‘big_loop’ is the actual thread routine being an infinite loop.
313 * It MUST be resumed via ‘ResumeThread’ by the server.
314 * When running it first checks if there is any request lingering
315 * in the client structure "sock", reads it out, processes it
316 * (can be blocking) and finally sends back a respond to the
317 * server.
318 *
319 * Unices:
320 * ‘big_loop’ is a infinite loop in a separate process. It reads
321 * blocking from a receive pipe, processes the request and puts the
322 * result to a sending pipe to the server.
323 *
324 * The coserver loop heavily differs in Win32 and Unices...
325 */
326
327 /* Debug info Macro. */
328 #if ENABLE_DEBUG
329 # define COSERVER_REQUEST_INFO() \
330 svz_log (SVZ_LOG_DEBUG, "%s: coserver request occurred\n", \
331 coservertypes[coserver->type].name);
332 #else
333 # define COSERVER_REQUEST_INFO()
334 #endif
335
336 /* Post-Processing Macro. */
337 #if ENABLE_DEBUG
338 # define COSERVER_RESULT() \
339 svz_log (SVZ_LOG_DEBUG, "%s: coserver request processed\n", \
340 coservertypes[coserver->type].name);
341 #else
342 # define COSERVER_RESULT()
343 #endif
344
345 /* Pre-Processing Macro. */
346 #define COSERVER_REQUEST() \
347 COSERVER_REQUEST_INFO (); \
348 /* Process the request here. Might be blocking indeed! */ \
349 if ((id = get_id (request)) != 0) \
350 { \
351 if ((result = coserver->callback (request)) == NULL) \
352 { \
353 result = request; \
354 *result = '\0'; \
355 } \
356 put_id (id, result); \
357 } \
358
359
360 #ifdef __MINGW32__
361 static void
big_loop(svz_coserver_t * coserver,svz_socket_t * sock)362 big_loop (svz_coserver_t *coserver, svz_socket_t *sock)
363 {
364 char *p;
365 int len;
366 char request[COSERVER_BUFSIZE];
367 char *result = NULL;
368 unsigned id;
369
370 /* wait until the thread handle has been passed */
371 while (svz_invalid_handle_p (coserver->thread));
372
373 /* infinite loop */
374 for (;;)
375 {
376 /* check if there is anything in the receive buffer */
377 while (sock->send_buffer_fill > 0)
378 {
379 p = sock->send_buffer;
380 while (*p != COSERVER_PACKET_BOUNDARY &&
381 p < sock->send_buffer + sock->send_buffer_fill)
382 p++;
383 len = p - sock->send_buffer + 1;
384
385 /* Copy the coserver request to static buffer. */
386 assert (len <= COSERVER_BUFSIZE);
387 memcpy (request, sock->send_buffer, len);
388
389 /* Enter a synchronized section (exclusive access to all data). */
390 EnterCriticalSection (&coserver->sync);
391 if (sock->send_buffer_fill > len)
392 {
393 memmove (sock->send_buffer, p + 1,
394 sock->send_buffer_fill - len);
395 }
396 sock->send_buffer_fill -= len;
397 LeaveCriticalSection (&coserver->sync);
398
399 COSERVER_REQUEST ();
400
401 if (id && result)
402 {
403 EnterCriticalSection (&coserver->sync);
404 memcpy (sock->recv_buffer + sock->recv_buffer_fill,
405 result, strlen (result));
406 sock->recv_buffer_fill += strlen (result);
407 LeaveCriticalSection (&coserver->sync);
408 COSERVER_RESULT ();
409 }
410 }
411
412 /* suspend myself and wait for being resumed ... */
413 if (SuspendThread (coserver->thread) == 0xFFFFFFFF)
414 {
415 svz_log_sys_error ("SuspendThread");
416 }
417 }
418 }
419
420 #else /* not __MINGW32__ */
421
422 static void
big_loop(svz_coserver_t * coserver,int in_pipe,int out_pipe)423 big_loop (svz_coserver_t *coserver, int in_pipe, int out_pipe)
424 {
425 FILE *in, *out;
426 char request[COSERVER_BUFSIZE];
427 char *result = NULL;
428 unsigned id;
429
430 if ((in = fdopen (in_pipe, "r")) == NULL)
431 {
432 svz_log_sys_error ("coserver: fdopen (%d)", in_pipe);
433 return;
434 }
435 if ((out = fdopen (out_pipe, "w")) == NULL)
436 {
437 svz_log_sys_error ("coserver: fdopen (%d)", out_pipe);
438 return;
439 }
440
441 while (NULL != fgets (request, COSERVER_BUFSIZE, in))
442 {
443
444 COSERVER_REQUEST ();
445
446 if (id && result)
447 {
448 fprintf (out, "%s", result);
449 fflush (out);
450 COSERVER_RESULT ();
451 }
452 }
453
454 /* error in reading pipe */
455 if (fclose (in))
456 svz_log_sys_error ("fclose");
457 if (fclose (out))
458 svz_log_sys_error ("fclose");
459 }
460
461 #endif /* not __MINGW32__ */
462
463 /*************************************************************************/
464 /* This is part of the server process. */
465 /*************************************************************************/
466
467 #ifdef __MINGW32__
468
469 /*
470 * This routine is the actual threads callback, but calls the coservers
471 * callback indeed. It is a wrapper routine for Win32, because you can pass
472 * only a single argument to a thread routine.
473 */
474 static DWORD WINAPI
coserver_thread(LPVOID thread)475 coserver_thread (LPVOID thread)
476 {
477 svz_coserver_t *coserver;
478
479 coserver = (svz_coserver_t *) thread;
480 big_loop (coserver, coserver->sock);
481 ExitThread (0);
482
483 return 0;
484 }
485
486 /*
487 * Reactivate all specific coservers with type @var{type}. In Win32
488 * you have to call this if you want the coserver start working.
489 */
490 static void
coserver_activate(int type)491 coserver_activate (int type)
492 {
493 size_t n;
494 int count = 0, res;
495 svz_coserver_t *coserver;
496
497 /* go through all internal coserver threads */
498 svz_array_foreach (coservers, coserver, n)
499 {
500 /* is this structure of the requested type? */
501 if (coserver->type == type)
502 {
503 /* activated the thread */
504 while ((res = ResumeThread (coserver->thread)) > 0);
505 if (res == 0)
506 count++;
507 }
508 }
509
510 #if ENABLE_DEBUG
511 svz_log (SVZ_LOG_DEBUG, "%d internal %s coserver activated\n",
512 count, coservertypes[type].name);
513 #endif /* ENABLE_DEBUG */
514 }
515
516 #endif /* __MINGW32__ */
517
518 /*
519 * Return the number of currently running coservers with the type @var{type}.
520 */
521 static int
count_type(int type)522 count_type (int type)
523 {
524 size_t n;
525 int count = 0;
526 svz_coserver_t *coserver;
527
528 svz_array_foreach (coservers, coserver, n)
529 if (coserver->type == type)
530 count++;
531 return count;
532 }
533
534 /*
535 * Delete the n'th internal coserver from coserver array.
536 */
537 static void
delete_nth(size_t n)538 delete_nth (size_t n)
539 {
540 svz_coserver_t *coserver;
541
542 if ((coserver = svz_array_get (coservers, n)) != NULL)
543 {
544 svz_free (coserver);
545 svz_array_del (coservers, n);
546 }
547 if (svz_array_size (coservers) == 0)
548 {
549 svz_array_destroy (coservers);
550 coservers = NULL;
551 }
552 }
553
554 #ifndef __MINGW32__
555 /*
556 * Disconnects a internal coserver. This is the callback routine for the
557 * socket structure entry `disconnected_socket'.
558 */
559 static int
disconnect(svz_socket_t * sock)560 disconnect (svz_socket_t *sock)
561 {
562 size_t n;
563 svz_coserver_t *coserver;
564
565 svz_array_foreach (coservers, coserver, n)
566 {
567 if (coserver->sock == sock)
568 {
569 #if ENABLE_DEBUG
570 svz_log (SVZ_LOG_DEBUG,
571 "%s: killing coserver pid %d\n",
572 coservertypes[coserver->type].name, coserver->pid);
573 #endif /* ENABLE_DEBUG */
574 if (kill (coserver->pid, SIGKILL) == -1)
575 svz_log_sys_error ("kill");
576 #if HAVE_WAITPID
577 /* cleanup coserver child process */
578 else if (waitpid (coserver->pid, NULL, WNOHANG) == -1)
579 svz_log_sys_error ("waitpid");
580 #endif /* HAVE_WAITPID */
581 /* re-arrange the internal coserver array */
582 delete_nth (n);
583 break;
584 }
585 }
586 return 0;
587 }
588 #endif /* not __MINGW32__ */
589
590 /*
591 * This routine has to be called for coservers requests. It is the default
592 * @code{check_request} routine for coservers detecting full responses as
593 * lines (trailing '\n').
594 */
595 static int
check_request(svz_socket_t * sock)596 check_request (svz_socket_t *sock)
597 {
598 char *packet = sock->recv_buffer;
599 char *p = packet;
600 int request_len;
601 int len = 0;
602 svz_coserver_t *coserver = svz_hash_get (friendly, svz_itoa (sock->id));
603
604 assert (coserver);
605 do
606 {
607 /* find a line (trailing '\n') */
608 while (*p != COSERVER_PACKET_BOUNDARY &&
609 p < sock->recv_buffer + sock->recv_buffer_fill)
610 p++;
611
612 if (*p == COSERVER_PACKET_BOUNDARY &&
613 p < sock->recv_buffer + sock->recv_buffer_fill)
614 {
615 coserver->busy--;
616 p++;
617 request_len = p - packet;
618 len += request_len;
619 if (sock->handle_request)
620 sock->handle_request (sock, packet, request_len);
621 packet = p;
622 }
623 }
624 while (p < sock->recv_buffer + sock->recv_buffer_fill);
625
626 #if ENABLE_DEBUG
627 svz_log (SVZ_LOG_DEBUG, "%s: %d byte response\n",
628 coservertypes[coserver->type].name, len);
629 #endif
630
631 /* remove data from receive buffer if necessary */
632 if (len > 0 && sock->recv_buffer_fill > len)
633 {
634 memmove (sock->recv_buffer, packet, sock->recv_buffer_fill - len);
635 }
636 sock->recv_buffer_fill -= len;
637
638 return 0;
639 }
640
641 /*
642 * The standard coserver @code{handle_request} routine is called whenever
643 * the standard @code{check_request} detected a full packet by any coserver.
644 */
645 static int
handle_request(UNUSED svz_socket_t * sock,char * request,int len)646 handle_request (UNUSED svz_socket_t *sock, char *request, int len)
647 {
648 int ret;
649 unsigned id;
650 char *p, *end, *data;
651 svz_coserver_callback_t *cb;
652
653 /* Search for coserver hash id. */
654 id = 0;
655 p = request;
656 end = p + len;
657 while (*p != COSERVER_ID_BOUNDARY && p < end)
658 {
659 if (*p < '0' || *p > '9')
660 {
661 svz_log (SVZ_LOG_WARNING,
662 "coserver: invalid character in id (0x%02X)\n", *p);
663 return -1;
664 }
665 id *= 10;
666 id += *p - '0';
667 p++;
668 }
669 if (p == end)
670 {
671 svz_log (SVZ_LOG_WARNING, "coserver: invalid coserver response (no id)\n");
672 return -1;
673 }
674 data = ++p;
675
676 /* Search for packet end. */
677 while (*p != COSERVER_PACKET_BOUNDARY && p < end)
678 p++;
679 if (p == end)
680 {
681 svz_log (SVZ_LOG_WARNING,
682 "coserver: invalid coserver response (no data)\n");
683 return -1;
684 }
685 *p = '\0';
686
687 /* Have a look at the coserver callback hash. */
688 if (NULL == (cb = svz_hash_get (callbacks, svz_itoa (id))))
689 {
690 svz_log (SVZ_LOG_ERROR, "coserver: invalid callback for id %u\n", id);
691 return -1;
692 }
693
694 /*
695 * Run the callback inclusive its arg. Second arg is either NULL for
696 * error detection or the actual result string. Afterwards free the
697 * callback structure and delete it from the coserver callback hash.
698 */
699 ret = cb->handle_result (*data ? data : NULL, cb->closure);
700 svz_hash_delete (callbacks, svz_itoa (id));
701 svz_free (cb);
702
703 return ret;
704 }
705
706 #ifndef __MINGW32__
707 /*
708 * This function closes the pipes (incoming and outgoing) of all coservers
709 * inherited to a newly instantiated coserver. These pipe descriptors are
710 * part of server process and are inherited when we call @code{fork} in
711 * order to create another coserver sub process. Since this coserver process
712 * should not access these pipes we are closing them.
713 */
714 static void
close_pipes(svz_coserver_t * self)715 close_pipes (svz_coserver_t *self)
716 {
717 size_t n;
718 svz_coserver_t *coserver;
719
720 /* go through all coservers except itself */
721 svz_array_foreach (coservers, coserver, n)
722 {
723 if (coserver != self)
724 {
725 close (coserver->sock->pipe_desc[SVZ_READ]);
726 close (coserver->sock->pipe_desc[SVZ_WRITE]);
727 }
728 }
729 }
730
731 /*
732 * Iterate each socket object and close its file/socket/pipe
733 * descriptors. Also frees the (cloned) queues.
734 * Note: Duplicate memory for everything else, including server private data.
735 * We cannot take care of all that because the servers do not know
736 * that they may get cloned. We therefore waste memory in the coservers.
737 */
738 static void
close_all(svz_socket_t * self)739 close_all (svz_socket_t *self)
740 {
741 svz_socket_t *sock, *next;
742
743 for (sock = svz_sock_root; sock != NULL; sock = next)
744 {
745 if (sock->flags & SVZ_SOFLG_SOCK)
746 if (sock->sock_desc >= 2)
747 close (sock->sock_desc);
748 if (sock->flags & SVZ_SOFLG_FILE)
749 if (sock->file_desc >= 2)
750 close (sock->file_desc);
751 if (sock->flags & SVZ_SOFLG_PIPE)
752 {
753 if (sock->pipe_desc[SVZ_READ] >= 2)
754 close (sock->pipe_desc[SVZ_READ]);
755 if (sock->pipe_desc[SVZ_WRITE] >= 2)
756 close (sock->pipe_desc[SVZ_WRITE]);
757 }
758 next = sock->next;
759 if (sock != self)
760 {
761 svz_sock_resize_buffers (sock, 0, 0);
762 svz_free (sock);
763 }
764 }
765 svz_file_closeall ();
766 }
767
768 /*
769 * Setup signaling for a coserver process. This is necessary since
770 * the original signal handlers get confused about signals raised by its
771 * children.
772 */
773 static void
setup_signals(void)774 setup_signals (void)
775 {
776 #ifdef SIGTERM
777 signal (SIGTERM, SIG_IGN);
778 #endif
779 #ifdef SIGINT
780 signal (SIGINT, SIG_IGN);
781 #endif
782 #ifdef SIGHUP
783 signal (SIGHUP, SIG_IGN);
784 #endif
785 #ifdef SIGPIPE
786 signal (SIGPIPE, SIG_IGN);
787 #endif
788 #ifdef SIGQUIT
789 signal (SIGQUIT, SIG_IGN);
790 #endif
791 }
792
793 #endif /* not __MINGW32__ */
794
795 /**
796 * Destroy specific coservers with the type @var{type}.
797 * All instances of this coserver type will be stopped.
798 */
799 void
svz_coserver_destroy(int type)800 svz_coserver_destroy (int type)
801 {
802 size_t n;
803 int count = 0;
804 svz_coserver_t *coserver;
805
806 svz_array_foreach (coservers, coserver, n)
807 {
808 if (coserver->type == type)
809 {
810 #ifdef __MINGW32__
811 /* stop the thread and close its handle */
812 if (!TerminateThread (coserver->thread, 0))
813 svz_log_sys_error ("TerminateThread");
814 if (!CloseHandle (coserver->thread))
815 svz_log_sys_error ("CloseHandle");
816 DeleteCriticalSection (&coserver->sync);
817
818 /* free all data reserved by the coserver */
819 svz_sock_free (coserver->sock);
820 #else /* not __MINGW32__ */
821 if (kill (coserver->pid, SIGKILL) == -1)
822 svz_log_sys_error ("kill");
823 #if HAVE_WAITPID
824 /* cleanup coserver child process */
825 else if (waitpid (coserver->pid, NULL, WNOHANG) == -1)
826 svz_log_sys_error ("waitpid");
827 #endif /* HAVE_WAITPID */
828 #endif /* not __MINGW32__ */
829 delete_nth (n);
830 n--;
831 count++;
832 }
833 }
834
835 #ifdef ENABLE_DEBUG
836 if (count > 0)
837 {
838 svz_log (SVZ_LOG_DEBUG, "%d internal %s coserver destroyed\n",
839 count, coservertypes[type].name);
840 }
841 #endif /* ENABLE_DEBUG */
842 }
843
844 /**
845 * Return the type name of @var{coserver}.
846 */
847 const char *
svz_coserver_type_name(const svz_coserver_t * coserver)848 svz_coserver_type_name (const svz_coserver_t *coserver)
849 {
850 return coservertypes[coserver->type].name;
851 }
852
853 /*
854 * Start a specific internal coserver. This works for Win32 and
855 * Unices. Whereas in Unix a process is @code{fork}ed and in Win32
856 * a thread gets started.
857 */
858 static svz_socket_t *
start(int type)859 start (int type)
860 {
861 svz_socket_t *sock;
862 svz_coserver_t *coserver;
863
864 #ifndef __MINGW32__
865 int s2c[2];
866 int c2s[2];
867 int pid;
868 #else /* not __MINGW32__ */
869 HANDLE thread;
870 DWORD tid;
871 #endif /* not __MINGW32__ */
872
873 svz_log (SVZ_LOG_NOTICE, "starting internal %s coserver\n",
874 coservertypes[type].name);
875
876 coserver = svz_malloc (sizeof (svz_coserver_t));
877 coserver->type = type;
878 coserver->busy = 0;
879 coserver->sock = NULL;
880
881 if (coservers == NULL)
882 coservers = svz_array_create (SVZ_MAX_COSERVER_TYPES, NULL);
883 svz_array_add (coservers, coserver);
884
885 /* fill in the actual coserver callback */
886 coserver->callback = coservertypes[type].callback;
887
888 #ifdef __MINGW32__
889 if ((sock = svz_sock_alloc ()) == NULL)
890 return NULL;
891
892 InitializeCriticalSection (&coserver->sync);
893 sock->write_socket = NULL;
894 sock->read_socket = NULL;
895 coserver->sock = sock;
896 svz_invalidate_handle (&coserver->thread);
897
898 if (svz_invalid_handle_p
899 (thread = CreateThread(
900 (LPSECURITY_ATTRIBUTES) NULL, /* ignore security attributes */
901 (DWORD) 0, /* default stack size */
902 (LPTHREAD_START_ROUTINE) coserver_thread, /* thread routine */
903 (LPVOID) coserver, /* thread argument */
904 (DWORD) CREATE_SUSPENDED, /* creation flags */
905 (LPDWORD) &tid))) /* thread id */
906 {
907 svz_log_sys_error ("CreateThread");
908 DeleteCriticalSection (&coserver->sync);
909 svz_sock_free (sock);
910 return NULL;
911 }
912
913 /* fill in thread access variables */
914 coserver->tid = tid;
915 coserver->thread = thread;
916
917 /* set thread priority */
918 if (!SetThreadPriority (thread, COSERVER_THREAD_PRIORITY))
919 svz_log_sys_error ("SetThreadPriority");
920
921 #ifdef ENABLE_DEBUG
922 svz_log (SVZ_LOG_DEBUG, "coserver thread id is 0x%08X\n", tid);
923 #endif
924
925 #else /* not __MINGW32__ */
926
927 /* create pipes for process communication */
928 if (pipe (s2c) < 0)
929 {
930 svz_log_sys_error ("pipe server-coserver");
931 delete_nth (svz_array_size (coservers) - 1);
932 return NULL;
933 }
934 if (pipe (c2s) < 0)
935 {
936 close (s2c[SVZ_READ]);
937 close (s2c[SVZ_WRITE]);
938 svz_log_sys_error ("pipe coserver-server");
939 delete_nth (svz_array_size (coservers) - 1);
940 return NULL;
941 }
942
943 /* ‘fork’ us here */
944 if ((pid = fork ()) == 0)
945 {
946 int in = s2c[SVZ_READ], out = c2s[SVZ_WRITE];
947
948 /* close the servers pipe descriptors */
949 if (close (s2c[SVZ_WRITE]) < 0)
950 svz_log_sys_error ("close");
951 if (close (c2s[SVZ_READ]) < 0)
952 svz_log_sys_error ("close");
953
954 #if ENABLE_DEBUG
955 svz_log (SVZ_LOG_DEBUG, "coserver pipes: %d-%d\n", in, out);
956 #endif
957
958 /* check if the pipes are 0, 1 or 2 already */
959 if (in > 2 && out > 2)
960 {
961 /* reassign the pipes to stdout and stdin */
962 if (dup2 (in, 0) != 0)
963 svz_log_sys_error ("dup2");
964 if (dup2 (out, 1) != 1)
965 svz_log_sys_error ("dup2");
966 /* close the old pipe descriptors */
967 close (in);
968 close (out);
969 close (2);
970 in = 0;
971 out = 1;
972 }
973 else
974 {
975 if (in != 2 && out != 2)
976 close (2);
977 if (in != 1 && out != 1)
978 close (1);
979 if (in != 0 && out != 0)
980 close (0);
981 }
982
983 /* close all other coserver pipes except its own */
984 close_pipes (coserver);
985 close_all (coserver->sock);
986 setup_signals ();
987
988 /* start the internal coserver */
989 big_loop (coserver, in, out);
990 exit (EXIT_SUCCESS);
991 }
992 else if (pid == -1)
993 {
994 svz_log_sys_error ("fork");
995 close (s2c[SVZ_READ]);
996 close (s2c[SVZ_WRITE]);
997 close (c2s[SVZ_READ]);
998 close (c2s[SVZ_WRITE]);
999 delete_nth (svz_array_size (coservers) - 1);
1000 return NULL;
1001 }
1002
1003 /* the old server process continues here */
1004
1005 #ifdef ENABLE_DEBUG
1006 svz_log (SVZ_LOG_DEBUG, "coserver process id is %d\n", pid);
1007 #endif
1008
1009 /* close the coservers pipe descriptors */
1010 if (close (s2c[SVZ_READ]) < 0)
1011 svz_log_sys_error ("close");
1012 if (close (c2s[SVZ_WRITE]) < 0)
1013 svz_log_sys_error ("close");
1014
1015 if ((sock = svz_pipe_create (c2s[SVZ_READ], s2c[SVZ_WRITE])) == NULL)
1016 {
1017 if (close (c2s[SVZ_READ]) < 0)
1018 svz_log_sys_error ("close");
1019 if (close (s2c[SVZ_WRITE]) < 0)
1020 svz_log_sys_error ("close");
1021 delete_nth (svz_array_size (coservers) - 1);
1022 return NULL;
1023 }
1024
1025 coserver->pid = pid;
1026 coserver->sock = sock;
1027 sock->disconnected_socket = disconnect;
1028 sock->write_socket = svz_pipe_write_socket;
1029 sock->read_socket = svz_pipe_read_socket;
1030 svz_sock_enqueue (sock);
1031
1032 #endif /* __MINGW32__ and Unices */
1033
1034 coservertypes[coserver->type].last_start = (long) time (NULL);
1035 svz_hash_put (friendly, svz_itoa (sock->id), coserver);
1036 sock->check_request = check_request;
1037 sock->handle_request = handle_request;
1038 sock->flags |= (SVZ_SOFLG_NOFLOOD | SVZ_SOFLG_COSERVER);
1039 return sock;
1040 }
1041
1042 /**
1043 * Under woe32 check if there was any response from an active coserver.
1044 * Moreover keep the coserver threads/processes alive. If one of the
1045 * coservers dies due to buffer overrun or might be overloaded,
1046 * start a new one.
1047 *
1048 * Call this function whenever there is time, e.g., within the timeout of the
1049 * @code{select} system call.
1050 */
1051 void
svz_coserver_check(void)1052 svz_coserver_check (void)
1053 {
1054 svz_coserver_t *coserver;
1055 svz_coservertype_t *ctype;
1056 svz_socket_t *sock;
1057 size_t n;
1058
1059 #ifdef __MINGW32__
1060 /* go through all coservers */
1061 svz_array_foreach (coservers, coserver, n)
1062 {
1063 sock = coserver->sock;
1064 while (sock->recv_buffer_fill > 0)
1065 {
1066 #if ENABLE_DEBUG
1067 svz_log (SVZ_LOG_DEBUG, "%s: coserver response detected\n",
1068 coservertypes[coserver->type].name);
1069 #endif
1070 /* find a full response within the receive buffer */
1071 if (sock->check_request)
1072 {
1073 EnterCriticalSection (&coserver->sync);
1074 sock->check_request (sock);
1075 LeaveCriticalSection (&coserver->sync);
1076 }
1077 }
1078 }
1079 #endif /* __MINGW32__ */
1080
1081 /* check the number of coserver instances of each coserver type */
1082 for (n = 0; n < SVZ_MAX_COSERVER_TYPES; n++)
1083 {
1084 ctype = &coservertypes[n];
1085 if (count_type (ctype->type) < ctype->instances &&
1086 ((long) time (NULL)) - ctype->last_start >= 3)
1087 start (ctype->type);
1088 }
1089
1090 /* restart coserver instances if buffer overrun is in sight (send buffer
1091 fill >= 75 percent) */
1092 svz_array_foreach (coservers, coserver, n)
1093 {
1094 ctype = &coservertypes[coserver->type];
1095 sock = coserver->sock;
1096 if (sock->send_buffer_fill * 100 / sock->send_buffer_size >= 75 &&
1097 ((long) time (NULL)) - ctype->last_start >= 3 &&
1098 count_type (ctype->type) <= ctype->instances)
1099 start (coserver->type);
1100 }
1101 }
1102
1103 /**
1104 * Create and return a single coserver with the given type @var{type}.
1105 */
1106 svz_coserver_t *
svz_coserver_create(int type)1107 svz_coserver_create (int type)
1108 {
1109 size_t n;
1110 svz_coserver_t *coserver = NULL;
1111
1112 if (coservertypes[type].init)
1113 coservertypes[type].init ();
1114 start (type);
1115 svz_array_foreach (coservers, coserver, n)
1116 {
1117 if (type == coserver->type)
1118 break;
1119 }
1120 return coserver;
1121 }
1122
1123 static void
forget_sock(const svz_socket_t * sock)1124 forget_sock (const svz_socket_t *sock)
1125 {
1126 svz_hash_delete (friendly, svz_itoa (sock->id));
1127 }
1128
1129 /*
1130 * Global coserver initialization. Here you should start all the internal
1131 * coservers you want to use later.
1132 */
1133 static int
init(void)1134 init (void)
1135 {
1136 int i, n;
1137 svz_coservertype_t *coserver;
1138
1139 friendly = svz_hash_create (1, NULL);
1140 svz_sock_prefree (1, forget_sock);
1141
1142 callbacks = svz_hash_create (4, svz_free);
1143 callback_id = 1;
1144
1145 for (n = 0; n < SVZ_MAX_COSERVER_TYPES; n++)
1146 {
1147 coserver = &coservertypes[n];
1148 if (coserver->init)
1149 coserver->init ();
1150 for (i = 0; i < coserver->instances; i++)
1151 start (coserver->type);
1152 }
1153
1154 return 0;
1155 }
1156
1157 /*
1158 * Global coserver finalization.
1159 */
1160 static int
finalize(void)1161 finalize (void)
1162 {
1163 int n;
1164 svz_coservertype_t *coserver;
1165
1166 for (n = 0; n < SVZ_MAX_COSERVER_TYPES; n++)
1167 {
1168 coserver = &coservertypes[n];
1169 svz_coserver_destroy (coserver->type);
1170 }
1171
1172 #if ENABLE_DEBUG
1173 svz_log (SVZ_LOG_DEBUG, "coserver: %d callback(s) left\n",
1174 svz_hash_size (callbacks));
1175 #endif
1176
1177 /* Destroy all callbacks left so far. */
1178 svz_hash_destroy (callbacks);
1179
1180 svz_sock_prefree (0, forget_sock);
1181 svz_hash_destroy (friendly);
1182 friendly = NULL;
1183
1184 return 0;
1185 }
1186
1187 /**
1188 * If @var{direction} is non-zero, init coserver internals.
1189 * Otherwise, finalize them. Return 0 if successful.
1190 *
1191 * If @var{direction} is positive, init also starts one instance each
1192 * of the builtin servers. If negative, it doesn't.
1193 */
1194 int
svz_updn_all_coservers(int direction)1195 svz_updn_all_coservers (int direction)
1196 {
1197 if (0 > direction)
1198 for (int i = 0; i < SVZ_MAX_COSERVER_TYPES; i++)
1199 coservertypes[i].instances = 0;
1200
1201 return (direction
1202 ? init
1203 : finalize)
1204 ();
1205 }
1206