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