📄 server-core.c
字号:
/* * server-core.c - server core implementation * * Copyright (C) 2000, 2001 Stefan Jahn <stefan@lkcc.org> * Copyright (C) 2000 Raimund Jacob <raimi@lkcc.org> * Copyright (C) 1999 Martin Grabmueller <mgrabmue@cs.tu-berlin.de> * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * $Id: server-core.c,v 1.30 2001/09/29 01:05:02 ela Exp $ * */#if HAVE_CONFIG_H# include <config.h>#endif#define _GNU_SOURCE#include <stdio.h>#include <stdlib.h>/* Some Unices define the strsignal() function depending on this definition. */#define __EXTENSIONS__#include <string.h>#include <errno.h>#include <fcntl.h>#include <sys/types.h>#include <signal.h>#include <time.h>#if HAVE_UNISTD_H# include <unistd.h>#endif#if HAVE_SYS_TIME_H# include <sys/time.h>#endif#if HAVE_SYS_RESOURCE_H && !defined (__MINGW32__)# include <sys/resource.h>#endif#ifdef __MINGW32__# include <winsock2.h>#endif#ifdef __MINGW32__# include <process.h>#else# include <sys/types.h># include <sys/socket.h># include <netinet/in.h># include <netdb.h># if HAVE_WAIT_H# include <wait.h># endif# if HAVE_SYS_WAIT_H# include <sys/wait.h># endif# if HAVE_STRINGS_H# include <strings.h># endif#endif#include "libserveez/alloc.h"#include "libserveez/util.h"#include "libserveez/snprintf.h"#include "libserveez/array.h"#include "libserveez/vector.h"#include "libserveez/socket.h"#include "libserveez/core.h"#include "libserveez/pipe-socket.h"#include "libserveez/server-loop.h"#include "libserveez/portcfg.h"#include "libserveez/coserver/coserver.h"#include "libserveez/server.h"#include "libserveez/server-core.h"/* * When @var{svz_nuke_happened} is set to a non-zero value, the server * will terminate its main loop. */int svz_nuke_happened;/* * When @var{svz_reset_happened} gets set to a non-zero value, the server * will try to re-initialize itself on the next execution of the main * loop. */static int svz_reset_happened;/* * The variable @var{svz_pipe_broke} is set to a non-zero value whenever * the server receives a SIGPIPE signal. */static int svz_pipe_broke;/* * @var{svz_child_died} is set to a non-zero value whenever the server * receives a SIGCHLD signal. */HANDLE svz_child_died;/* * This holds the time on which the next call to @code{svz_periodic_tasks()} * should occur. */long svz_notify;/* * @var{svz_sock_root} is the pointer to the head of the list of sockets, * which are handled by the server loop. */svz_socket_t *svz_sock_root = NULL;/* * @var{svz_sock_last} always points to the last structure in the socket queue * and is @var{NULL} when the queue is empty. */svz_socket_t *svz_sock_last = NULL;/* * The @var{svz_sock_lookup_table} array is used to speed up references to * socket structures by socket's id. */static svz_socket_t **svz_sock_lookup_table = NULL;static int svz_sock_id = 0;static int svz_sock_version = 0;static int svz_sock_limit = 1024; /* Must be binary size ! *//* Pointer to argv[0]. */static char *svz_executable_file = NULL;/* * Set the name of the executable file which uses the core library. This * is usually @code{argv[0]}. */voidsvz_executable (char *file){ svz_executable_file = file;}#ifdef SIGSEGV#define SIGSEGV_TEXT \ "\nFatal error (access violation)." \ "\nPlease report this bug to <bug-serveez@gnu.org>." \ "\nIf possible, please try to obtain a C stack backtrace via\n" \ "\n $ gdb %s core" \ "\n $ (gdb) where\n" \ "\nand include this info into your bug report. If you do not have gdb" \ "\ninstalled you can also try dbx. Also tell us your architecture and" \ "\noperating system you are currently working on.\n\n"#endif#if defined (SIGSEGV)/* * Segmentation fault exception handler. */RETSIGTYPEsvz_segfault_exception (int sig){#if HAVE_GETRLIMIT struct rlimit rlim; rlim.rlim_max = RLIM_INFINITY; rlim.rlim_cur = RLIM_INFINITY; setrlimit (RLIMIT_CORE, &rlim);#endif /* HAVE_GETRLIMIT */ signal (sig, SIG_DFL); fprintf (stderr, SIGSEGV_TEXT, svz_executable_file ? svz_executable_file : "binary"); raise (sig);}#endif /* SIGSEGV *//* * Handle some signals to handle server resets (SIGHUP), to ignore * broken pipes (SIGPIPE) and to exit gracefully if requested by the * user (SIGINT, SIGTERM). */RETSIGTYPE svz_signal_handler (int sig){ switch (sig) {#ifdef SIGHUP case SIGHUP: svz_reset_happened = 1; signal (SIGHUP, svz_signal_handler); break;#endif#ifdef SIGPIPE case SIGPIPE: svz_pipe_broke = 1; signal (SIGPIPE, svz_signal_handler); break;#endif#ifdef SIGCHLD case SIGCHLD:#if HAVE_WAITPID { int status, pid; /* check if the child has been just stopped */ if ((pid = waitpid (-1, &status, WNOHANG | WUNTRACED)) != -1) { if (!WIFSTOPPED (status)) svz_child_died = pid; } }#else /* HAVE_WAITPID */ if ((svz_child_died = wait (NULL)) == -1) svz_child_died = 0;#endif /* not HAVE_WAITPID */ signal (SIGCHLD, svz_signal_handler); break;#endif#ifdef SIGBREAK case SIGBREAK: /* * reset signal handlers to the default, so the server * can get killed on second try */ svz_nuke_happened = 1; signal (SIGBREAK, SIG_DFL); break;#endif#ifdef SIGTERM case SIGTERM: svz_nuke_happened = 1; signal (SIGTERM, SIG_DFL); break;#endif#ifdef SIGINT case SIGINT: svz_nuke_happened = 1; signal (SIGINT, SIG_DFL); break;#endif#ifdef SIGQUIT case SIGQUIT: svz_nuke_happened = 1; signal (SIGQUIT, SIG_DFL); break;#endif default: svz_log (LOG_DEBUG, "uncaught signal %d\n", sig); break; } svz_log (LOG_WARNING, "signal: %s\n", svz_strsignal (sig));#ifdef NONVOID_SIGNAL return 0;#endif}/* 65 is hopefully a safe bet, kill(1) accepts 0..64, *sigh* */#define SVZ_NUMBER_OF_SIGNALS 65/* Cached results of strsignal calls. */static svz_array_t *svz_signal_strings = NULL;/* On some platforms strsignal() can be resolved but is nowhere declared. */#if defined (HAVE_STRSIGNAL) && !defined (DECLARED_STRSIGNAL)extern char * strsignal (int);#endif/* * Prepare library so that @code{svz_strsignal()} works. Called * from @code{svz_boot()}. */voidsvz_strsignal_init (void){ int i; char *str; const char *format = "Signal %d"; if (svz_signal_strings != NULL) return; svz_signal_strings = svz_array_create (SVZ_NUMBER_OF_SIGNALS); for (i = 0; i < SVZ_NUMBER_OF_SIGNALS; i++) {#if HAVE_STRSIGNAL if (NULL == (str = (char *) strsignal (i))) { str = svz_malloc (128); svz_snprintf (str, 128, format, i); svz_array_add (svz_signal_strings, svz_strdup (str)); svz_free (str); } else { svz_array_add (svz_signal_strings, svz_strdup (str)); }#else /* not HAVE_STRSIGNAL */ str = svz_malloc (128); svz_snprintf (str, 128, format, i); svz_array_add (svz_signal_strings, svz_strdup (str)); svz_free (str);#endif /* HAVE_STRSIGNAL */ }}/* * The function @code{svz_strsignal()} does not work afterwards anymore. * Called from @code{svz_halt()}. */voidsvz_strsignal_destroy (void){ int i; char *value; svz_array_foreach (svz_signal_strings, value, i) svz_free (value); svz_array_destroy (svz_signal_strings); svz_signal_strings = NULL;}/* * Resolve the given signal number to form a describing string. * This function is reentrant, use it from the signal handler. * It does not return NULL. The returned pointer is shared amongst * all users. For unknown signals (which is not supposed to happen) * the returned string points to a statically allocated buffer (which * destroys the reentrance, of course) [who cares :-]. */char *svz_strsignal (int sig){ static char fallback[128]; if (sig >= 0 && sig < SVZ_NUMBER_OF_SIGNALS) return (char *) svz_array_get (svz_signal_strings, sig); else { svz_snprintf (fallback, 128, "Impossible signal %d", sig); return fallback; }}/* * Abort the process, printing the error message @var{msg} first. */static intsvz_abort (char *msg){ svz_log (LOG_FATAL, "list validation failed: %s\n", msg); abort (); return 0;}#if ENABLE_DEBUG/* * This function is for debugging purposes only. It shows a text * representation of the current socket list. */static voidsvz_sock_print_list (void){ svz_socket_t *sock = svz_sock_root; while (sock) { fprintf (stdout, "id: %04d, sock: %p == %p, prev: %p, next: %p\n", sock->id, (void *) sock, (void *) svz_sock_lookup_table[sock->id], (void *) sock->prev, (void *) sock->next); sock = sock->next; } fprintf (stdout, "\n");}/* * Go through the socket list and check if it is still consistent. * Abort the program with an error message, if it is not. */static intsvz_sock_validate_list (void){ svz_socket_t *sock, *prev; #if 0 svz_sock_print_list ();#endif prev = NULL; sock = svz_sock_root; while (sock) { /* check if the descriptors are valid */ if (sock->flags & SOCK_FLAG_SOCK) { if (svz_sock_valid (sock) == -1) { svz_abort ("invalid socket descriptor"); } } if (sock->flags & SOCK_FLAG_PIPE) { if (svz_pipe_valid (sock) == -1) { svz_abort ("invalid pipe descriptor"); } } /* check socket list structure */ if (svz_sock_lookup_table[sock->id] != sock) { svz_abort ("lookup table corrupted"); } if (prev != sock->prev) { svz_abort ("list structure invalid (sock->prev)"); } prev = sock; sock = sock->next; } if (prev != svz_sock_last) { svz_abort ("list structure invalid (last socket)"); } return 0;}#endif /* ENABLE_DEBUG *//* * Rechain the socket list to prevent sockets from starving at the end * of this list. We will call it every time when a @code{select()} or * @code{poll()} has returned. Listeners are kept at the beginning of the * chain anyway. */static voidsvz_sock_rechain_list (void){ svz_socket_t *sock; svz_socket_t *last_listen; svz_socket_t *end_socket; sock = svz_sock_last; if (sock && sock->prev) { end_socket = sock->prev; for (last_listen = svz_sock_root; last_listen && last_listen != sock && last_listen->flags & (SOCK_FLAG_LISTENING | SOCK_FLAG_PRIORITY) && !(sock->flags & SOCK_FLAG_LISTENING); last_listen = last_listen->next); /* just listeners in the list, return */ if (!last_listen) return; /* sock is the only non-listening (connected) socket */ if (sock == last_listen) return; /* one step back unless we are at the socket root */ if (last_listen->prev) { last_listen = last_listen->prev; /* put sock in front of chain behind listeners */ sock->next = last_listen->next; sock->next->prev = sock; /* put sock behind last listener */ last_listen->next = sock; sock->prev = last_listen; } else { /* enqueue at root */ sock->next = svz_sock_root; sock->prev = NULL; sock->next->prev = sock; svz_sock_root = sock; } /* mark the new end of chain */ end_socket->next = NULL; svz_sock_last = end_socket; }}/* * Enqueue the socket @var{sock} into the list of sockets handled by * the server loop. */intsvz_sock_enqueue (svz_socket_t *sock){ /* check for validity of pipe descriptors */ if (sock->flags & SOCK_FLAG_PIPE) { if (svz_pipe_valid (sock) == -1) { svz_log (LOG_FATAL, "cannot enqueue invalid pipe\n"); return -1; } } /* check for validity of socket descriptors */ if (sock->flags & SOCK_FLAG_SOCK) { if (svz_sock_valid (sock) == -1) { svz_log (LOG_FATAL, "cannot enqueue invalid socket\n"); return -1; } } /* check lookup table */ if (svz_sock_lookup_table[sock->id] || sock->flags & SOCK_FLAG_ENQUEUED) { svz_log (LOG_FATAL, "socket id %d has been already enqueued\n", sock->id); return -1; } /* really enqueue socket */ sock->next = NULL; sock->prev = NULL; if (!svz_sock_root) { svz_sock_root = sock; } else { svz_sock_last->next = sock; sock->prev = svz_sock_last; } svz_sock_last = sock; sock->flags |= SOCK_FLAG_ENQUEUED; svz_sock_lookup_table[sock->id] = sock; return 0;}/* * Remove the socket @var{sock} from the list of sockets handled by * the server loop. */intsvz_sock_dequeue (svz_socket_t *sock){ /* check for validity of pipe descriptors */ if (sock->flags & SOCK_FLAG_PIPE) { if (svz_pipe_valid (sock) == -1) { svz_log (LOG_FATAL, "cannot dequeue invalid pipe\n"); return -1; } } /* check for validity of socket descriptors */ if (sock->flags & SOCK_FLAG_SOCK) { if (svz_sock_valid (sock) == -1) { svz_log (LOG_FATAL, "cannot dequeue invalid socket\n"); return -1; } } /* check lookup table */ if (!svz_sock_lookup_table[sock->id] || !(sock->flags & SOCK_FLAG_ENQUEUED)) { svz_log (LOG_FATAL, "socket id %d has been already dequeued\n", sock->id); return -1; } /* really dequeue socket */ if (sock->next) sock->next->prev = sock->prev; else svz_sock_last = sock->prev; if (sock->prev) sock->prev->next = sock->next; else svz_sock_root = sock->next; sock->flags &= ~SOCK_FLAG_ENQUEUED; svz_sock_lookup_table[sock->id] = NULL; return 0;}/* * This routine checks the connection frequency of the socket structure * @var{child} for the port configuration of the listener socket structure * @var{parent}. Returns zero if the connection frequency is valid, * otherwise non-zero. */intsvz_sock_check_frequency (svz_socket_t *parent, svz_socket_t *child){ svz_portcfg_t *port = parent->port; char *ip = svz_inet_ntoa (child->remote_addr); time_t *t, current;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -