mirror of
https://github.com/stefanocasazza/ULib.git
synced 2025-09-28 19:05:55 +08:00
2907 lines
97 KiB
C++
2907 lines
97 KiB
C++
// ============================================================================
|
|
//
|
|
// = LIBRARY
|
|
// ULib - c++ library
|
|
//
|
|
// = FILENAME
|
|
// server.cpp
|
|
//
|
|
// = AUTHOR
|
|
// Stefano Casazza
|
|
//
|
|
// ============================================================================
|
|
|
|
#include <ulib/url.h>
|
|
#include <ulib/net/udpsocket.h>
|
|
#include <ulib/utility/escape.h>
|
|
#include <ulib/orm/orm_driver.h>
|
|
#include <ulib/dynamic/dynamic.h>
|
|
#include <ulib/utility/services.h>
|
|
#include <ulib/net/server/server.h>
|
|
#include <ulib/utility/string_ext.h>
|
|
|
|
#ifdef _MSWINDOWS_
|
|
# include <ws2tcpip.h>
|
|
#else
|
|
# include <pwd.h>
|
|
# include <ulib/net/unixsocket.h>
|
|
#endif
|
|
|
|
#ifdef U_STATIC_HANDLER_RPC
|
|
# include <ulib/net/server/plugin/mod_rpc.h>
|
|
#endif
|
|
#ifdef U_STATIC_HANDLER_SHIB
|
|
# include <ulib/net/server/plugin/mod_shib.h>
|
|
#endif
|
|
#ifdef U_STATIC_HANDLER_ECHO
|
|
# include <ulib/net/server/plugin/mod_echo.h>
|
|
#endif
|
|
#ifdef U_STATIC_HANDLER_STREAM
|
|
# include <ulib/net/server/plugin/mod_stream.h>
|
|
#endif
|
|
#ifdef U_STATIC_HANDLER_NOCAT
|
|
# include <ulib/net/server/plugin/mod_nocat.h>
|
|
#endif
|
|
#ifdef U_STATIC_HANDLER_SOCKET
|
|
# include <ulib/net/server/plugin/mod_socket.h>
|
|
#endif
|
|
#ifdef U_STATIC_HANDLER_SCGI
|
|
# include <ulib/net/server/plugin/mod_scgi.h>
|
|
#endif
|
|
#ifdef U_STATIC_HANDLER_FCGI
|
|
# include <ulib/net/server/plugin/mod_fcgi.h>
|
|
#endif
|
|
#ifdef U_STATIC_HANDLER_GEOIP
|
|
# include <ulib/net/server/plugin/mod_geoip.h>
|
|
#endif
|
|
#ifdef U_STATIC_HANDLER_PROXY
|
|
# include <ulib/net/server/plugin/mod_proxy.h>
|
|
#endif
|
|
#ifdef U_STATIC_HANDLER_SOAP
|
|
# include <ulib/net/server/plugin/mod_soap.h>
|
|
#endif
|
|
#ifdef U_STATIC_HANDLER_SSI
|
|
# include <ulib/net/server/plugin/mod_ssi.h>
|
|
#endif
|
|
#ifdef U_STATIC_HANDLER_TSA
|
|
# include <ulib/net/server/plugin/mod_tsa.h>
|
|
#endif
|
|
#ifdef U_STATIC_HANDLER_HTTP
|
|
# include <ulib/net/server/plugin/mod_http.h>
|
|
#endif
|
|
|
|
#define U_DEFAULT_PORT 80
|
|
|
|
int UServer_Base::rkids;
|
|
int UServer_Base::timeoutMS;
|
|
int UServer_Base::iAddressType;
|
|
int UServer_Base::verify_mode;
|
|
int UServer_Base::preforked_num_kids;
|
|
bool UServer_Base::bssl;
|
|
bool UServer_Base::bipc;
|
|
bool UServer_Base::binsert;
|
|
bool UServer_Base::flag_loop;
|
|
bool UServer_Base::flag_sigterm;
|
|
bool UServer_Base::public_address;
|
|
bool UServer_Base::monitoring_process;
|
|
bool UServer_Base::set_tcp_keep_alive;
|
|
bool UServer_Base::set_realtime_priority;
|
|
bool UServer_Base::update_date1;
|
|
bool UServer_Base::update_date2;
|
|
bool UServer_Base::update_date3;
|
|
char UServer_Base::mod_name[2][16];
|
|
ULog* UServer_Base::log;
|
|
ULog* UServer_Base::apache_like_log;
|
|
pid_t UServer_Base::pid;
|
|
char* UServer_Base::client_address;
|
|
ULock* UServer_Base::lock_user1;
|
|
ULock* UServer_Base::lock_user2;
|
|
time_t UServer_Base::last_event;
|
|
int32_t UServer_Base::oClientImage;
|
|
uint32_t UServer_Base::map_size;
|
|
uint32_t UServer_Base::max_depth;
|
|
uint32_t UServer_Base::vplugin_size;
|
|
uint32_t UServer_Base::shared_data_add;
|
|
uint32_t UServer_Base::client_address_len;
|
|
uint32_t UServer_Base::wakeup_for_nothing;
|
|
uint32_t UServer_Base::document_root_size;
|
|
uint32_t UServer_Base::num_client_for_parallelization;
|
|
sigset_t UServer_Base::mask;
|
|
UString* UServer_Base::host;
|
|
UString* UServer_Base::server;
|
|
UString* UServer_Base::as_user;
|
|
UString* UServer_Base::dh_file;
|
|
UString* UServer_Base::ca_file;
|
|
UString* UServer_Base::ca_path;
|
|
UString* UServer_Base::key_file;
|
|
UString* UServer_Base::password;
|
|
UString* UServer_Base::cert_file;
|
|
UString* UServer_Base::name_sock;
|
|
UString* UServer_Base::IP_address;
|
|
UString* UServer_Base::cenvironment;
|
|
UString* UServer_Base::senvironment;
|
|
UString* UServer_Base::document_root;
|
|
UString* UServer_Base::str_preforked_num_kids;
|
|
USocket* UServer_Base::socket;
|
|
UProcess* UServer_Base::proc;
|
|
UEventFd* UServer_Base::handler_inotify;
|
|
UEventTime* UServer_Base::ptime;
|
|
const char* UServer_Base::document_root_ptr;
|
|
unsigned int UServer_Base::port;
|
|
UFileConfig* UServer_Base::cfg;
|
|
UServer_Base* UServer_Base::pthis;
|
|
|
|
UVector<UString>* UServer_Base::vplugin_name;
|
|
UVector<UString>* UServer_Base::vplugin_name_static;
|
|
UClientImage_Base* UServer_Base::pClientIndex;
|
|
UClientImage_Base* UServer_Base::vClientImage;
|
|
UClientImage_Base* UServer_Base::eClientImage;
|
|
ULog::static_date* UServer_Base::ptr_static_date;
|
|
UVector<UServerPlugIn*>* UServer_Base::vplugin;
|
|
UServer_Base::shared_data* UServer_Base::ptr_shared_data;
|
|
UVector<UServer_Base::file_LOG*>* UServer_Base::vlog;
|
|
|
|
#if defined(USE_LIBSSL) && defined(ENABLE_THREAD)
|
|
ULock* UServer_Base::lock_ocsp_staple;
|
|
#endif
|
|
#ifdef U_WELCOME_SUPPORT
|
|
UString* UServer_Base::msg_welcome;
|
|
#endif
|
|
#ifdef U_ACL_SUPPORT
|
|
UString* UServer_Base::allow_IP;
|
|
UVector<UIPAllow*>* UServer_Base::vallow_IP;
|
|
#endif
|
|
#ifdef U_RFC1918_SUPPORT
|
|
bool UServer_Base::enable_rfc1918_filter;
|
|
UString* UServer_Base::allow_IP_prv;
|
|
UVector<UIPAllow*>* UServer_Base::vallow_IP_prv;
|
|
#endif
|
|
|
|
#ifdef ENABLE_THREAD
|
|
# include <ulib/thread.h>
|
|
|
|
class UTimeThread : public UThread {
|
|
public:
|
|
|
|
UTimeThread() : UThread(true, false) { watch_counter = 1; }
|
|
|
|
#ifdef DEBUG
|
|
UTimeVal before;
|
|
#endif
|
|
int watch_counter;
|
|
|
|
virtual void run()
|
|
{
|
|
U_TRACE(0, "UTimeThread::run()")
|
|
|
|
# ifdef DEBUG
|
|
long delta;
|
|
UTimeVal after;
|
|
# endif
|
|
bool bchange;
|
|
struct timespec ts;
|
|
long tv_sec_old = u_now->tv_sec;
|
|
|
|
U_SRV_LOG("UTimeThread optimization for time resolution of one second activated (pid %u)", UThread::getTID());
|
|
|
|
while (UServer_Base::flag_loop)
|
|
{
|
|
ts.tv_sec = 1L;
|
|
ts.tv_nsec = 0L;
|
|
|
|
(void) U_SYSCALL(nanosleep, "%p,%p", &ts, 0);
|
|
|
|
# if defined(U_LOG_ENABLE) && defined(USE_LIBZ)
|
|
if ((UServer_Base::log && UServer_Base::log->checkForLogRotateDataToWrite()) ||
|
|
(UServer_Base::apache_like_log && UServer_Base::apache_like_log->checkForLogRotateDataToWrite()))
|
|
{
|
|
watch_counter = 1;
|
|
}
|
|
# endif
|
|
|
|
U_INTERNAL_DUMP("watch_counter = %d tv_sec_old = %ld u_now->tv_sec = %ld", watch_counter, tv_sec_old, u_now->tv_sec)
|
|
|
|
if (tv_sec_old == u_now->tv_sec)
|
|
{
|
|
if (--watch_counter > 0) u_now->tv_sec++;
|
|
else
|
|
{
|
|
# ifdef DEBUG
|
|
if (watch_counter == 0)
|
|
{
|
|
before.tv_sec = u_now->tv_sec + 1;
|
|
before.tv_usec = u_now->tv_usec;
|
|
}
|
|
# endif
|
|
|
|
(void) U_SYSCALL(gettimeofday, "%p,%p", u_now, 0);
|
|
|
|
# ifdef DEBUG
|
|
after.set(*u_now);
|
|
|
|
after -= before;
|
|
delta = after.getMilliSecond();
|
|
|
|
if (delta >= 1000L ||
|
|
delta <= -1000L)
|
|
{
|
|
U_SRV_LOG("UTimeThread delta time exceed 1 sec: diff(%ld ms)", delta);
|
|
|
|
if (delta <= -30000L) U_ERROR("UTimeThread delta time exceed too much - ts = { %ld, %ld }", ts.tv_sec, ts.tv_nsec);
|
|
}
|
|
# endif
|
|
|
|
watch_counter = 30;
|
|
|
|
if (tv_sec_old == u_now->tv_sec) continue;
|
|
}
|
|
}
|
|
|
|
U_INTERNAL_ASSERT_DIFFERS(u_now->tv_sec, tv_sec_old)
|
|
|
|
bchange = ((u_now->tv_sec % U_ONE_HOUR_IN_SECOND) == 0);
|
|
|
|
if (UServer_Base::update_date1)
|
|
{
|
|
if (bchange == false) UTimeDate::updateTime(U_HTTP_DATE1 + 12);
|
|
else (void) u_strftime2(U_HTTP_DATE1, 17, "%d/%m/%y %T", u_now->tv_sec + u_now_adjust);
|
|
}
|
|
|
|
if (UServer_Base::update_date2)
|
|
{
|
|
if (bchange == false) UTimeDate::updateTime(U_HTTP_DATE2 + 15);
|
|
else (void) u_strftime2(U_HTTP_DATE2, 26-6, "%d/%b/%Y:%T", u_now->tv_sec + u_now_adjust); // NB: %z in general don't change...
|
|
}
|
|
|
|
if (UServer_Base::update_date3)
|
|
{
|
|
if (bchange == false) UTimeDate::updateTime(U_HTTP_DATE3 + 20);
|
|
else (void) u_strftime2(U_HTTP_DATE3, 29-4, "%a, %d %b %Y %T", u_now->tv_sec); // GMT can't change...
|
|
}
|
|
|
|
tv_sec_old = u_now->tv_sec;
|
|
}
|
|
}
|
|
};
|
|
|
|
class UClientThread : public UThread {
|
|
public:
|
|
|
|
UClientThread() : UThread(true, false) {}
|
|
|
|
virtual void run()
|
|
{
|
|
U_TRACE(0, "UClientThread::run()")
|
|
|
|
while (UServer_Base::flag_loop) UNotifier::waitForEvent(UServer_Base::ptime);
|
|
}
|
|
};
|
|
#endif
|
|
|
|
#ifndef _MSWINDOWS_
|
|
static int sysctl_somaxconn, tcp_abort_on_overflow, sysctl_max_syn_backlog, tcp_fin_timeout;
|
|
#endif
|
|
|
|
UServer_Base::UServer_Base(UFileConfig* pcfg)
|
|
{
|
|
U_TRACE_REGISTER_OBJECT(0, UServer_Base, "%p", pcfg)
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(pthis, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(cenvironment, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(senvironment, 0)
|
|
|
|
port = U_DEFAULT_PORT;
|
|
pthis = this;
|
|
|
|
as_user = U_NEW(UString);
|
|
dh_file = U_NEW(UString);
|
|
cert_file = U_NEW(UString);
|
|
key_file = U_NEW(UString);
|
|
password = U_NEW(UString);
|
|
ca_file = U_NEW(UString);
|
|
ca_path = U_NEW(UString);
|
|
name_sock = U_NEW(UString);
|
|
IP_address = U_NEW(UString);
|
|
document_root = U_NEW(UString);
|
|
|
|
vlog = U_NEW(UVector<file_LOG*>);
|
|
cenvironment = U_NEW(UString(U_CAPACITY));
|
|
senvironment = U_NEW(UString(U_CAPACITY));
|
|
|
|
u_init_ulib_hostname();
|
|
u_init_ulib_username();
|
|
|
|
u_init_security();
|
|
|
|
if (u_start_time == 0 &&
|
|
u_setStartTime() == false)
|
|
{
|
|
U_WARNING("System date not updated: %#5D", u_now->tv_sec);
|
|
}
|
|
|
|
if (pcfg)
|
|
{
|
|
U_INTERNAL_ASSERT_EQUALS(cfg, 0)
|
|
|
|
cfg = pcfg;
|
|
|
|
cfg->load();
|
|
|
|
if (cfg->empty() == false) loadConfigParam();
|
|
}
|
|
|
|
#ifdef ENABLE_IPV6
|
|
if (UClientImage_Base::bIPv6) iAddressType = AF_INET6;
|
|
else
|
|
#endif
|
|
iAddressType = AF_INET;
|
|
}
|
|
|
|
UServer_Base::~UServer_Base()
|
|
{
|
|
U_TRACE_UNREGISTER_OBJECT(0, UServer_Base)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(socket)
|
|
U_INTERNAL_ASSERT_POINTER(vplugin)
|
|
|
|
#ifdef ENABLE_THREAD
|
|
if (u_pthread_time)
|
|
{
|
|
((UTimeThread*)u_pthread_time)->suspend();
|
|
|
|
delete (UTimeThread*)u_pthread_time; // delete to join
|
|
}
|
|
#endif
|
|
|
|
UClientImage_Base::clear();
|
|
|
|
delete socket;
|
|
delete vplugin;
|
|
delete vplugin_name;
|
|
|
|
UOrmDriver::clear();
|
|
|
|
U_INTERNAL_ASSERT_POINTER(cenvironment)
|
|
U_INTERNAL_ASSERT_POINTER(senvironment)
|
|
U_INTERNAL_ASSERT_EQUALS(handler_inotify, 0)
|
|
|
|
delete cenvironment;
|
|
delete senvironment;
|
|
|
|
if (host) delete host;
|
|
if (ptime) delete ptime;
|
|
|
|
#ifdef U_WELCOME_SUPPORT
|
|
if (msg_welcome) delete msg_welcome;
|
|
#endif
|
|
|
|
#ifdef U_ACL_SUPPORT
|
|
if (vallow_IP)
|
|
{
|
|
delete allow_IP;
|
|
delete vallow_IP;
|
|
}
|
|
#endif
|
|
|
|
#ifdef U_RFC1918_SUPPORT
|
|
if (vallow_IP_prv)
|
|
{
|
|
delete allow_IP_prv;
|
|
delete vallow_IP_prv;
|
|
}
|
|
#endif
|
|
|
|
#ifndef _MSWINDOWS_
|
|
if (as_user->empty() &&
|
|
isChild() == false)
|
|
{
|
|
u_need_root(false);
|
|
|
|
(void) UFile::setSysParam("/proc/sys/net/ipv4/tcp_fin_timeout", tcp_fin_timeout, true);
|
|
|
|
if (USocket::iBackLog >= SOMAXCONN)
|
|
{
|
|
(void) UFile::setSysParam("/proc/sys/net/core/somaxconn", sysctl_somaxconn, true);
|
|
(void) UFile::setSysParam("/proc/sys/net/ipv4/tcp_max_syn_backlog", sysctl_max_syn_backlog, true);
|
|
}
|
|
|
|
if (USocket::iBackLog == 1) (void) UFile::setSysParam("/proc/sys/net/ipv4/tcp_abort_on_overflow", tcp_abort_on_overflow, true);
|
|
}
|
|
#endif
|
|
|
|
if (proc) delete proc;
|
|
if (server) delete server;
|
|
if (lock_user1) delete lock_user1;
|
|
if (lock_user2) delete lock_user2;
|
|
if (USemaphore::flock) delete USemaphore::flock;
|
|
|
|
UFile::munmap(ptr_shared_data, map_size);
|
|
|
|
delete as_user;
|
|
delete dh_file;
|
|
delete cert_file;
|
|
delete key_file;
|
|
delete password;
|
|
delete ca_file;
|
|
delete ca_path;
|
|
delete name_sock;
|
|
delete IP_address;
|
|
delete document_root;
|
|
|
|
UEventFd::fd = -1; // NB: to avoid to delete itself...
|
|
|
|
UNotifier::num_connection = 0;
|
|
|
|
UDynamic::clear();
|
|
UNotifier::clear();
|
|
UPlugIn<void*>::clear();
|
|
}
|
|
|
|
void UServer_Base::closeLog()
|
|
{
|
|
U_TRACE(0, "UServer_Base::closeLog()")
|
|
|
|
#ifdef U_LOG_ENABLE
|
|
if (log &&
|
|
log->isOpen())
|
|
{
|
|
ULog::close();
|
|
|
|
# ifdef DEBUG
|
|
delete log;
|
|
# endif
|
|
|
|
log = 0;
|
|
}
|
|
|
|
if (apache_like_log &&
|
|
apache_like_log->isOpen())
|
|
{
|
|
apache_like_log->closeLog();
|
|
|
|
# ifdef DEBUG
|
|
delete apache_like_log;
|
|
# endif
|
|
|
|
apache_like_log = 0;
|
|
}
|
|
#endif
|
|
|
|
if (vlog)
|
|
{
|
|
for (uint32_t i = 0, n = vlog->size(); i < n; ++i)
|
|
{
|
|
file_LOG* item = (*vlog)[i];
|
|
|
|
if (item->LOG->isOpen()) item->LOG->close();
|
|
|
|
delete item->LOG;
|
|
}
|
|
|
|
vlog->clear();
|
|
|
|
# ifdef DEBUG
|
|
delete vlog;
|
|
# endif
|
|
|
|
vlog = 0;
|
|
}
|
|
}
|
|
|
|
#ifdef U_WELCOME_SUPPORT
|
|
void UServer_Base::setMsgWelcome(const UString& msg)
|
|
{
|
|
U_TRACE(0, "UServer_Base::setMsgWelcome(%.*S)", U_STRING_TO_TRACE(msg))
|
|
|
|
U_INTERNAL_ASSERT(msg)
|
|
|
|
msg_welcome = U_NEW(UString(U_CAPACITY));
|
|
|
|
if (UEscape::decode(msg, *msg_welcome)) (void) msg_welcome->shrink();
|
|
else
|
|
{
|
|
delete msg_welcome;
|
|
msg_welcome = 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void UServer_Base::loadConfigParam()
|
|
{
|
|
U_TRACE(0, "UServer_Base::loadConfigParam()")
|
|
|
|
U_INTERNAL_ASSERT_POINTER(cfg)
|
|
|
|
// --------------------------------------------------------------------------------------------------------------------------------------
|
|
// userver - configuration parameters
|
|
// --------------------------------------------------------------------------------------------------------------------------------------
|
|
// ENABLE_IPV6 flag indicating the use of ipv6
|
|
// SERVER host name or ip address for the listening socket
|
|
// PORT port number for the listening socket
|
|
// SOCKET_NAME file name for the listening socket
|
|
// IP_ADDRESS ip address of host for the interface connected to the Internet (autodetected if not specified)
|
|
//
|
|
// ALLOWED_IP list of comma separated client address for IP-based access control (IPADDR[/MASK])
|
|
// ALLOWED_IP_PRIVATE list of comma separated client private address for IP-based access control (IPADDR[/MASK]) for public server
|
|
// ENABLE_RFC1918_FILTER reject request from private IP to public server address
|
|
//
|
|
// LISTEN_BACKLOG max number of ready to be delivered connections to accept()
|
|
// SET_REALTIME_PRIORITY flag indicating that the preforked processes will be scheduled under the real-time policies SCHED_FIFO
|
|
// CLIENT_FOR_PARALLELIZATION min number of clients to active parallelization
|
|
//
|
|
// PID_FILE write pid on file indicated
|
|
// WELCOME_MSG message of welcome to send initially to client
|
|
// RUN_AS_USER downgrade the security to that of user account
|
|
// DOCUMENT_ROOT The directory out of which you will serve your documents
|
|
//
|
|
// LOG_FILE locations for file log
|
|
// LOG_FILE_SZ memory size for file log
|
|
// LOG_MSG_SIZE limit length of print network message to LOG_MSG_SIZE chars (default 128)
|
|
//
|
|
// PLUGIN list of plugins to load, a flexible way to add specific functionality to the server
|
|
// PLUGIN_DIR directory where there are plugins to load
|
|
//
|
|
// ORM_DRIVER list of ORM drivers to load, a flexible way to add specific functionality to the ORM
|
|
// ORM_DRIVER_DIR directory where there are ORM drivers to load
|
|
//
|
|
// REQ_TIMEOUT timeout for request from client
|
|
// TCP_KEEP_ALIVE Specifies to active the TCP keepalive implementation in the linux kernel.
|
|
// MAX_KEEP_ALIVE Specifies the maximum number of requests that can be served through a Keep-Alive (Persistent) session.
|
|
// (Value <= 0 will disable Keep-Alive)
|
|
//
|
|
// DH_FILE DH param
|
|
// CERT_FILE server certificate
|
|
// KEY_FILE server private key
|
|
// PASSWORD password for server private key
|
|
// CA_FILE locations of trusted CA certificates used in the verification
|
|
// CA_PATH locations of trusted CA certificates used in the verification
|
|
// VERIFY_MODE mode of verification (SSL_VERIFY_NONE=0, SSL_VERIFY_PEER=1, SSL_VERIFY_FAIL_IF_NO_PEER_CERT=2, SSL_VERIFY_CLIENT_ONCE=4)
|
|
// CIPHER_SUITE cipher suite model (Intermediate=0, Modern=1, Old=2)
|
|
//
|
|
// PREFORK_CHILD number of child server processes created at startup: -1 - thread approach (experimental)
|
|
// 0 - serialize, no forking
|
|
// 1 - classic, forking after client accept
|
|
// >1 - pool of serialized processes plus monitoring process
|
|
// --------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
#ifdef USE_LIBSSL
|
|
U_INTERNAL_DUMP("bssl = %b", bssl)
|
|
#endif
|
|
|
|
#if !defined(HAVE_EPOLL_WAIT) && !defined(USE_LIBEVENT) && defined(DEBUG)
|
|
U_INTERNAL_DUMP("SOMAXCONN = %d FD_SETSIZE = %d", SOMAXCONN, FD_SETSIZE)
|
|
#endif
|
|
|
|
UString x = (*cfg)[*UString::str_SERVER];
|
|
*name_sock = (*cfg)[*UString::str_SOCKET_NAME];
|
|
|
|
if (x) server = U_NEW(UString(x));
|
|
|
|
*IP_address = cfg->at(U_CONSTANT_TO_PARAM("IP_ADDRESS"));
|
|
|
|
#ifdef ENABLE_IPV6
|
|
UClientImage_Base::bIPv6 = cfg->readBoolean(U_CONSTANT_TO_PARAM("ENABLE_IPV6"));
|
|
#endif
|
|
|
|
timeoutMS = cfg->readLong(U_CONSTANT_TO_PARAM("REQ_TIMEOUT"), -1);
|
|
|
|
if (timeoutMS > 0) timeoutMS *= 1000;
|
|
|
|
port = cfg->readLong(*UString::str_PORT, U_DEFAULT_PORT);
|
|
|
|
if (port == U_DEFAULT_PORT &&
|
|
UServices::isSetuidRoot() == false)
|
|
{
|
|
port = 8080;
|
|
|
|
U_WARNING("Sorry, it is required root privilege to listen on port 80 but I am not setuid root, I must try 8080");
|
|
}
|
|
|
|
USocket::iBackLog = cfg->readLong(U_CONSTANT_TO_PARAM("LISTEN_BACKLOG"), SOMAXCONN);
|
|
set_tcp_keep_alive = cfg->readBoolean(U_CONSTANT_TO_PARAM("TCP_KEEP_ALIVE"));
|
|
set_realtime_priority = cfg->readBoolean(U_CONSTANT_TO_PARAM("SET_REALTIME_PRIORITY"), true);
|
|
UNotifier::max_connection = cfg->readLong(U_CONSTANT_TO_PARAM("MAX_KEEP_ALIVE"));
|
|
u_printf_string_max_length = cfg->readLong(U_CONSTANT_TO_PARAM("LOG_MSG_SIZE"));
|
|
num_client_for_parallelization = cfg->readLong(U_CONSTANT_TO_PARAM("CLIENT_FOR_PARALLELIZATION"));
|
|
|
|
x = cfg->at(U_CONSTANT_TO_PARAM("PREFORK_CHILD"));
|
|
|
|
if (x) str_preforked_num_kids = U_NEW(UString(x));
|
|
|
|
#ifdef U_WELCOME_SUPPORT
|
|
x = cfg->at(U_CONSTANT_TO_PARAM("WELCOME_MSG"));
|
|
|
|
if (x) setMsgWelcome(x);
|
|
#endif
|
|
|
|
#ifdef USE_LIBSSL
|
|
*password = (*cfg)[*UString::str_PASSWORD];
|
|
*ca_file = (*cfg)[*UString::str_CA_FILE];
|
|
*ca_path = (*cfg)[*UString::str_CA_PATH];
|
|
*key_file = (*cfg)[*UString::str_KEY_FILE];
|
|
*cert_file = (*cfg)[*UString::str_CERT_FILE];
|
|
|
|
*dh_file = cfg->at(U_CONSTANT_TO_PARAM("DH_FILE"));
|
|
verify_mode = cfg->readLong(*UString::str_VERIFY_MODE);
|
|
#endif
|
|
|
|
// Instructs server to accept connections from the IP address IPADDR. A CIDR mask length can be
|
|
// supplied optionally after a trailing slash, e.g. 192.168.0.0/24, in which case addresses that
|
|
// match in the most significant MASK bits will be allowed. If no options are specified, all clients
|
|
// are allowed. Unauthorized connections are rejected by closing the TCP connection immediately. A
|
|
// warning is logged on the server but nothing is sent to the client
|
|
|
|
#ifdef U_ACL_SUPPORT
|
|
x = cfg->at(U_CONSTANT_TO_PARAM("ALLOWED_IP"));
|
|
|
|
if (x)
|
|
{
|
|
allow_IP = U_NEW(UString(x));
|
|
vallow_IP = U_NEW(UVector<UIPAllow*>);
|
|
|
|
if (UIPAllow::parseMask(*allow_IP, *vallow_IP) == 0)
|
|
{
|
|
delete allow_IP;
|
|
allow_IP = 0;
|
|
|
|
delete vallow_IP;
|
|
vallow_IP = 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef U_RFC1918_SUPPORT
|
|
x = cfg->at(U_CONSTANT_TO_PARAM("ALLOWED_IP_PRIVATE"));
|
|
|
|
if (x)
|
|
{
|
|
allow_IP_prv = U_NEW(UString(x));
|
|
vallow_IP_prv = U_NEW(UVector<UIPAllow*>);
|
|
|
|
if (UIPAllow::parseMask(*allow_IP_prv, *vallow_IP_prv) == 0)
|
|
{
|
|
delete allow_IP_prv;
|
|
allow_IP_prv = 0;
|
|
|
|
delete vallow_IP_prv;
|
|
vallow_IP_prv = 0;
|
|
}
|
|
}
|
|
|
|
enable_rfc1918_filter = cfg->readBoolean(U_CONSTANT_TO_PARAM("ENABLE_RFC1918_FILTER"));
|
|
#endif
|
|
|
|
// write pid on file...
|
|
|
|
x = (*cfg)[*UString::str_PID_FILE];
|
|
|
|
if (x) (void) UFile::writeTo(x, UString(u_pid_str, u_pid_str_len));
|
|
|
|
// If you want the webserver to run as a process of a defined user, you can do it.
|
|
// For the change of user to work, it's necessary to execute the server with root privileges.
|
|
// If it's started by a user that that doesn't have root privileges, this step will be omitted
|
|
|
|
#ifdef U_LOG_ENABLE
|
|
bool bmsg = false;
|
|
#endif
|
|
|
|
x = cfg->at(U_CONSTANT_TO_PARAM("RUN_AS_USER"));
|
|
|
|
if (x)
|
|
{
|
|
if (UServices::isSetuidRoot())
|
|
{
|
|
U_INTERNAL_ASSERT(x.isNullTerminated())
|
|
|
|
struct passwd* pw = (struct passwd*) U_SYSCALL(getpwnam, "%S", x.data());
|
|
|
|
if (pw &&
|
|
pw->pw_dir)
|
|
{
|
|
*as_user = x;
|
|
|
|
# ifdef DEBUG
|
|
UMemoryPool::obj_class = "";
|
|
UMemoryPool::func_call = __PRETTY_FUNCTION__;
|
|
# endif
|
|
|
|
char* buffer = (char*)UMemoryPool::pop(U_SIZE_TO_STACK_INDEX(U_PATH_MAX));
|
|
|
|
(void) snprintf(buffer, U_PATH_MAX, "HOME=%s", pw->pw_dir);
|
|
|
|
(void) U_SYSCALL(putenv, "%S", buffer);
|
|
|
|
# ifdef DEBUG
|
|
UMemoryPool::obj_class = UMemoryPool::func_call = 0;
|
|
# endif
|
|
}
|
|
}
|
|
# ifdef U_LOG_ENABLE
|
|
else bmsg = true;
|
|
# endif
|
|
}
|
|
|
|
// DOCUMENT_ROOT: The directory out of which you will serve your documents
|
|
|
|
*document_root = cfg->at(U_CONSTANT_TO_PARAM("DOCUMENT_ROOT"));
|
|
|
|
if (document_root->empty() ||
|
|
document_root->equal(U_CONSTANT_TO_PARAM(".")))
|
|
{
|
|
(void) document_root->replace(u_cwd, (document_root_size = u_cwd_len));
|
|
}
|
|
else
|
|
{
|
|
U_INTERNAL_ASSERT(document_root->isNullTerminated())
|
|
|
|
document_root_ptr = document_root->data();
|
|
|
|
char c = *document_root_ptr;
|
|
|
|
if (c == '~' ||
|
|
c == '$')
|
|
{
|
|
*document_root = UStringExt::expandPath(*document_root, 0);
|
|
|
|
document_root_ptr = document_root->data();
|
|
|
|
if (document_root->empty()) U_ERROR("var DOCUMENT_ROOT %S expansion failed", document_root_ptr);
|
|
}
|
|
|
|
*document_root = UFile::getRealPath(document_root_ptr, false);
|
|
|
|
document_root_size = document_root->size();
|
|
}
|
|
|
|
document_root_ptr = document_root->data();
|
|
|
|
U_INTERNAL_DUMP("document_root(%u) = %.*S", document_root_size, document_root_size, document_root_ptr)
|
|
|
|
if (UFile::chdir(document_root_ptr, false) == false) U_ERROR("chdir to working directory (DOCUMENT_ROOT) %S failed", document_root_ptr);
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(document_root_size, u_cwd_len)
|
|
U_INTERNAL_ASSERT_EQUALS(strncmp(document_root_ptr, u_cwd, u_cwd_len), 0)
|
|
|
|
#ifdef U_LOG_ENABLE
|
|
x = (*cfg)[*UString::str_LOG_FILE];
|
|
|
|
if (x)
|
|
{
|
|
// open log
|
|
|
|
update_date1 = true;
|
|
|
|
log = U_NEW(ULog(x, cfg->readLong(*UString::str_LOG_FILE_SZ)));
|
|
|
|
log->init(U_SERVER_LOG_PREFIX);
|
|
|
|
U_SRV_LOG("Working directory (DOCUMENT_ROOT) changed to %.*S", u_cwd_len, u_cwd);
|
|
|
|
if (bmsg) U_SRV_LOG("WARNING: the \"RUN_AS_USER\" directive makes sense only if the master process runs with super-user privileges, ignored");
|
|
}
|
|
#endif
|
|
|
|
UString plugin_dir = cfg->at(U_CONSTANT_TO_PARAM("PLUGIN_DIR"));
|
|
|
|
#if defined(USE_SQLITE) || defined(USE_MYSQL) || defined(USE_PGSQL)
|
|
UString orm_driver_dir = cfg->at(U_CONSTANT_TO_PARAM("ORM_DRIVER_DIR")),
|
|
orm_driver_list = cfg->at(U_CONSTANT_TO_PARAM("ORM_DRIVER"));
|
|
|
|
if (orm_driver_dir) UOrmDriver::setDriverDirectory(orm_driver_dir);
|
|
|
|
// load ORM driver modules...
|
|
|
|
if (orm_driver_list &&
|
|
UOrmDriver::loadDriver(orm_driver_list) == false)
|
|
{
|
|
U_ERROR("ORM drivers load failed");
|
|
}
|
|
#endif
|
|
|
|
// load plugin modules and call server-wide hooks handlerConfig()...
|
|
|
|
UString plugin_list = cfg->at(U_CONSTANT_TO_PARAM("PLUGIN"));
|
|
|
|
if (loadPlugins(plugin_dir, plugin_list) == U_PLUGIN_HANDLER_ERROR) U_ERROR("Plugins stage load failed");
|
|
}
|
|
|
|
// load plugin modules and call server-wide hooks handlerConfig()...
|
|
|
|
U_NO_EXPORT void UServer_Base::loadStaticLinkedModules(const char* name)
|
|
{
|
|
U_TRACE(0, "UServer_Base::loadStaticLinkedModules(%S)", name)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(vplugin_name)
|
|
U_INTERNAL_ASSERT_MAJOR(vplugin_size, 0)
|
|
|
|
UString x(name);
|
|
|
|
if (vplugin_name->find(x) != U_NOT_FOUND) // NB: we load only the plugin that we want from configuration (PLUGIN var)...
|
|
{
|
|
# ifdef U_LOG_ENABLE
|
|
const char* fmt = "WARNING: Link phase of static plugin %s failed\n";
|
|
# endif
|
|
|
|
#if defined(U_STATIC_HANDLER_RPC) || defined(U_STATIC_HANDLER_SHIB) || defined(U_STATIC_HANDLER_ECHO) || \
|
|
defined(U_STATIC_HANDLER_STREAM) || defined(U_STATIC_HANDLER_SOCKET) || defined(U_STATIC_HANDLER_SCGI) || \
|
|
defined(U_STATIC_HANDLER_FCGI) || defined(U_STATIC_HANDLER_GEOIP) || defined(U_STATIC_HANDLER_PROXY) || \
|
|
defined(U_STATIC_HANDLER_SOAP) || defined(U_STATIC_HANDLER_SSI) || defined(U_STATIC_HANDLER_TSA) || \
|
|
defined(U_STATIC_HANDLER_NOCAT) || defined(U_STATIC_HANDLER_HTTP)
|
|
const UServerPlugIn* _plugin = 0;
|
|
# ifdef U_STATIC_HANDLER_RPC
|
|
if (x.equal(U_CONSTANT_TO_PARAM("rpc"))) { _plugin = U_NEW(URpcPlugIn); goto next; }
|
|
# endif
|
|
# ifdef U_STATIC_HANDLER_SHIB
|
|
if (x.equal(U_CONSTANT_TO_PARAM("shib"))) { _plugin = U_NEW(UShibPlugIn); goto next; }
|
|
# endif
|
|
# ifdef U_STATIC_HANDLER_ECHO
|
|
if (x.equal(U_CONSTANT_TO_PARAM("echo"))) { _plugin = U_NEW(UEchoPlugIn); goto next; }
|
|
# endif
|
|
# ifdef U_STATIC_HANDLER_STREAM
|
|
if (x.equal(U_CONSTANT_TO_PARAM("stream"))) { _plugin = U_NEW(UStreamPlugIn); goto next; }
|
|
# endif
|
|
# ifdef U_STATIC_HANDLER_SOCKET
|
|
if (x.equal(U_CONSTANT_TO_PARAM("socket"))) { _plugin = U_NEW(UWebSocketPlugIn); goto next; }
|
|
# endif
|
|
# ifdef U_STATIC_HANDLER_SCGI
|
|
if (x.equal(U_CONSTANT_TO_PARAM("scgi"))) { _plugin = U_NEW(USCGIPlugIn); goto next; }
|
|
# endif
|
|
# ifdef U_STATIC_HANDLER_FCGI
|
|
if (x.equal(U_CONSTANT_TO_PARAM("fcgi"))) { _plugin = U_NEW(UFCGIPlugIn); goto next; }
|
|
# endif
|
|
# ifdef U_STATIC_HANDLER_GEOIP
|
|
if (x.equal(U_CONSTANT_TO_PARAM("geoip"))) { _plugin = U_NEW(UGeoIPPlugIn); goto next; }
|
|
# endif
|
|
# ifdef U_STATIC_HANDLER_PROXY
|
|
if (x.equal(U_CONSTANT_TO_PARAM("proxy"))) { _plugin = U_NEW(UProxyPlugIn); goto next; }
|
|
# endif
|
|
# ifdef U_STATIC_HANDLER_SOAP
|
|
if (x.equal(U_CONSTANT_TO_PARAM("soap"))) { _plugin = U_NEW(USoapPlugIn); goto next; }
|
|
# endif
|
|
# ifdef U_STATIC_HANDLER_SSI
|
|
if (x.equal(U_CONSTANT_TO_PARAM("ssi"))) { _plugin = U_NEW(USSIPlugIn); goto next; }
|
|
# endif
|
|
# ifdef U_STATIC_HANDLER_TSA
|
|
if (x.equal(U_CONSTANT_TO_PARAM("tsa"))) { _plugin = U_NEW(UTsaPlugIn); goto next; }
|
|
# endif
|
|
# ifdef U_STATIC_HANDLER_NOCAT
|
|
if (x.equal(U_CONSTANT_TO_PARAM("nocat"))) { _plugin = U_NEW(UNoCatPlugIn); goto next; }
|
|
# endif
|
|
# ifdef U_STATIC_HANDLER_HTTP
|
|
if (x.equal(U_CONSTANT_TO_PARAM("http"))) { _plugin = U_NEW(UHttpPlugIn); goto next; }
|
|
# endif
|
|
next:
|
|
if (_plugin)
|
|
{
|
|
vplugin->push_back(_plugin);
|
|
vplugin_name_static->push_back(x);
|
|
|
|
# ifdef U_LOG_ENABLE
|
|
fmt = "Link phase of static plugin %s success";
|
|
# endif
|
|
}
|
|
#endif
|
|
|
|
# ifdef U_LOG_ENABLE
|
|
if (isLog()) ULog::log(fmt, name);
|
|
# endif
|
|
}
|
|
}
|
|
|
|
int UServer_Base::loadPlugins(UString& plugin_dir, const UString& plugin_list)
|
|
{
|
|
U_TRACE(0, "UServer_Base::loadPlugins(%.*S,%.*S)", U_STRING_TO_TRACE(plugin_dir), U_STRING_TO_TRACE(plugin_list))
|
|
|
|
if (plugin_dir)
|
|
{
|
|
// NB: we can't use relativ path because after we call chdir()...
|
|
|
|
if (plugin_dir.first_char() == '.')
|
|
{
|
|
U_INTERNAL_ASSERT(plugin_dir.isNullTerminated())
|
|
|
|
plugin_dir = UFile::getRealPath(plugin_dir.data());
|
|
}
|
|
|
|
UDynamic::setPluginDirectory(plugin_dir);
|
|
}
|
|
|
|
vplugin = U_NEW(UVector<UServerPlugIn*>(10U));
|
|
vplugin_name = U_NEW(UVector<UString>(10U));
|
|
vplugin_name_static = U_NEW(UVector<UString>(20U));
|
|
|
|
uint32_t i, pos;
|
|
UString item, _name;
|
|
UServerPlugIn* _plugin;
|
|
int result = U_PLUGIN_HANDLER_ERROR;
|
|
|
|
if (plugin_list.empty())
|
|
{
|
|
vplugin_size = 1;
|
|
|
|
vplugin_name->push_back(*UString::str_http);
|
|
}
|
|
else
|
|
{
|
|
UClientImage_Base::callerHandlerRequest = pluginsHandlerRequest;
|
|
|
|
// NB: we don't use split with substr() cause of dependency from config var PLUGIN...
|
|
|
|
vplugin_size = vplugin_name->split(U_STRING_TO_PARAM(plugin_list));
|
|
}
|
|
|
|
/**
|
|
* I do know that to include code in the middle of a function is hacky and dirty,
|
|
* but this is the best solution that I could figure out. If you have some idea to
|
|
* clean it up, please, don't hesitate and let me know
|
|
*/
|
|
|
|
# include "plugin/loader.autoconf.cpp"
|
|
|
|
for (i = 0; i < vplugin_size; ++i)
|
|
{
|
|
item = vplugin_name->at(i);
|
|
pos = vplugin_name_static->find(item);
|
|
|
|
U_INTERNAL_DUMP("i = %u pos = %u item = %.*S", i, pos, U_STRING_TO_TRACE(item))
|
|
|
|
if (pos != U_NOT_FOUND) continue;
|
|
|
|
_name.setBuffer(32U);
|
|
|
|
_name.snprintf("server_plugin_%.*s", U_STRING_TO_TRACE(item));
|
|
|
|
_plugin = UPlugIn<UServerPlugIn*>::create(U_STRING_TO_PARAM(_name));
|
|
|
|
# ifdef U_LOG_ENABLE
|
|
if (isLog())
|
|
{
|
|
(void) u__snprintf(mod_name[0], sizeof(mod_name[0]), "[%.*s] ", U_STRING_TO_TRACE(item));
|
|
|
|
if (_plugin == 0) ULog::log("%sWARNING: Load phase of plugin %.*s failed", mod_name[0], U_STRING_TO_TRACE(item));
|
|
else ULog::log("%sLoad phase of plugin %.*s success", mod_name[0], U_STRING_TO_TRACE(item));
|
|
|
|
mod_name[0][0] = '\0';
|
|
}
|
|
# endif
|
|
|
|
if (_plugin)
|
|
{
|
|
vplugin->insert(i, _plugin);
|
|
vplugin_name_static->insert(i, item);
|
|
}
|
|
}
|
|
|
|
U_INTERNAL_ASSERT_MAJOR(vplugin_size, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(vplugin->size(), vplugin_size)
|
|
U_INTERNAL_ASSERT_EQUALS(*vplugin_name, *vplugin_name_static)
|
|
|
|
delete vplugin_name_static;
|
|
|
|
UClientImage_Base::callerHandlerRead = pluginsHandlerREAD;
|
|
|
|
if (cfg)
|
|
{
|
|
// NB: we load configuration in reverse order respect to config var PLUGIN...
|
|
|
|
i = vplugin_size;
|
|
|
|
do {
|
|
item = vplugin_name->at(--i);
|
|
|
|
if (cfg->searchForObjectStream(U_STRING_TO_PARAM(item)))
|
|
{
|
|
cfg->table.clear();
|
|
|
|
_plugin = vplugin->at(i);
|
|
|
|
# ifdef U_LOG_ENABLE
|
|
if (isLog()) (void) u__snprintf(mod_name[0], sizeof(mod_name[0]), "[%.*s] ", U_STRING_TO_TRACE(item));
|
|
# endif
|
|
|
|
result = _plugin->handlerConfig(*cfg);
|
|
|
|
# ifdef U_LOG_ENABLE
|
|
if (isLog())
|
|
{
|
|
if ((result & (U_PLUGIN_HANDLER_ERROR | U_PLUGIN_HANDLER_PROCESSED)) != 0)
|
|
{
|
|
const char* fmt = ((result & U_PLUGIN_HANDLER_ERROR) == 0
|
|
? "%sConfiguration phase of plugin %.*s success"
|
|
: "%sWARNING: Configuration phase of plugin %.*s failed");
|
|
|
|
ULog::log(fmt, mod_name[0], U_STRING_TO_TRACE(item));
|
|
}
|
|
|
|
mod_name[0][0] = '\0';
|
|
}
|
|
# endif
|
|
|
|
cfg->reset();
|
|
|
|
if ((result & U_PLUGIN_HANDLER_GO_ON) == 0) U_RETURN(result);
|
|
}
|
|
|
|
if (i == 0) U_RETURN(U_PLUGIN_HANDLER_FINISHED);
|
|
}
|
|
while (true);
|
|
}
|
|
|
|
U_RETURN(U_PLUGIN_HANDLER_FINISHED);
|
|
}
|
|
|
|
// manage plugin handler hooks...
|
|
|
|
#ifdef U_LOG_DISABLE
|
|
# define U_PLUGIN_HANDLER(xxx) \
|
|
int UServer_Base::pluginsHandler##xxx() \
|
|
{ \
|
|
U_TRACE(0, "UServer_Base::pluginsHandler"#xxx"()") \
|
|
\
|
|
U_INTERNAL_ASSERT_POINTER(vplugin) \
|
|
U_INTERNAL_ASSERT_MAJOR(vplugin_size, 0) \
|
|
\
|
|
int result; \
|
|
uint32_t i = 0; \
|
|
\
|
|
do { \
|
|
result = vplugin->at(i)->handler##xxx(); \
|
|
\
|
|
if ((result & U_PLUGIN_HANDLER_GO_ON) == 0) U_RETURN(result); \
|
|
} \
|
|
while (++i < vplugin_size); \
|
|
\
|
|
U_RETURN(U_PLUGIN_HANDLER_FINISHED); \
|
|
}
|
|
#else
|
|
# define U_PLUGIN_HANDLER(xxx) \
|
|
int UServer_Base::pluginsHandler##xxx() \
|
|
{ \
|
|
U_TRACE(0, "UServer_Base::pluginsHandler"#xxx"()") \
|
|
\
|
|
U_INTERNAL_ASSERT_POINTER(vplugin) \
|
|
U_INTERNAL_ASSERT_MAJOR(vplugin_size, 0) \
|
|
\
|
|
int result; \
|
|
uint32_t i = 0; \
|
|
const char* fmt; \
|
|
UServerPlugIn* _plugin; \
|
|
\
|
|
do { \
|
|
_plugin = vplugin->at(i); \
|
|
\
|
|
if (isLog() == false) result = _plugin->handler##xxx(); \
|
|
else \
|
|
{ \
|
|
UString name = vplugin_name->at(i); \
|
|
\
|
|
(void) u__snprintf(mod_name[0], sizeof(mod_name[0]), \
|
|
"[%.*s] ", U_STRING_TO_TRACE(name)); \
|
|
\
|
|
result = _plugin->handler##xxx(); \
|
|
\
|
|
if ((result & (U_PLUGIN_HANDLER_ERROR | \
|
|
U_PLUGIN_HANDLER_PROCESSED)) != 0) \
|
|
{ \
|
|
if ((result & U_PLUGIN_HANDLER_ERROR) != 0) \
|
|
{ \
|
|
fmt = ((result & U_PLUGIN_HANDLER_FINISHED) != 0 \
|
|
? 0 \
|
|
: "%sWARNING: "#xxx" phase of plugin %.*s failed"); \
|
|
} \
|
|
else \
|
|
{ \
|
|
fmt = (U_ClientImage_parallelization == 2 || \
|
|
(result & U_PLUGIN_HANDLER_PROCESSED) == 0 \
|
|
? 0 \
|
|
: "%s"#xxx" phase of plugin %.*s success"); \
|
|
} \
|
|
\
|
|
if (fmt) ULog::log(fmt,mod_name[0],U_STRING_TO_TRACE(name));\
|
|
} \
|
|
\
|
|
mod_name[0][0] = '\0'; \
|
|
} \
|
|
\
|
|
if ((result & U_PLUGIN_HANDLER_GO_ON) == 0) U_RETURN(result); \
|
|
} \
|
|
while (++i < vplugin_size); \
|
|
\
|
|
U_RETURN(U_PLUGIN_HANDLER_FINISHED); \
|
|
}
|
|
#endif
|
|
|
|
// Connection-wide hooks
|
|
U_PLUGIN_HANDLER(Request)
|
|
U_PLUGIN_HANDLER(Reset)
|
|
|
|
// NB: we call the various handlerXXX() in reverse order respect to config var PLUGIN...
|
|
|
|
#ifdef U_LOG_DISABLE
|
|
# define U_PLUGIN_HANDLER_REVERSE(xxx) \
|
|
int UServer_Base::pluginsHandler##xxx() \
|
|
{ \
|
|
U_TRACE(0, "UServer_Base::pluginsHandler"#xxx"()") \
|
|
\
|
|
U_INTERNAL_ASSERT_POINTER(vplugin) \
|
|
U_INTERNAL_ASSERT_MAJOR(vplugin_size, 0) \
|
|
\
|
|
int result; \
|
|
uint32_t i = vplugin_size; \
|
|
\
|
|
do { \
|
|
result = vplugin->at(--i)->handler##xxx(); \
|
|
\
|
|
if ((result & U_PLUGIN_HANDLER_GO_ON) == 0) U_RETURN(result); \
|
|
\
|
|
if (i == 0) U_RETURN(U_PLUGIN_HANDLER_FINISHED); \
|
|
} \
|
|
while (true); \
|
|
}
|
|
#else
|
|
# define U_PLUGIN_HANDLER_REVERSE(xxx) \
|
|
int UServer_Base::pluginsHandler##xxx() \
|
|
{ \
|
|
U_TRACE(0, "UServer_Base::pluginsHandler"#xxx"()") \
|
|
\
|
|
U_INTERNAL_ASSERT_POINTER(vplugin) \
|
|
U_INTERNAL_ASSERT_MAJOR(vplugin_size, 0) \
|
|
\
|
|
int result; \
|
|
const char* fmt; \
|
|
UServerPlugIn* _plugin; \
|
|
uint32_t i = vplugin_size; \
|
|
\
|
|
do { \
|
|
_plugin = vplugin->at(--i); \
|
|
\
|
|
if (isLog() == false) result = _plugin->handler##xxx(); \
|
|
else \
|
|
{ \
|
|
UString name = vplugin_name->at(i); \
|
|
\
|
|
(void) u__snprintf(mod_name[0], sizeof(mod_name[0]), \
|
|
"[%.*s] ", U_STRING_TO_TRACE(name)); \
|
|
\
|
|
result = _plugin->handler##xxx(); \
|
|
\
|
|
if ((result & (U_PLUGIN_HANDLER_ERROR | \
|
|
U_PLUGIN_HANDLER_PROCESSED)) != 0) \
|
|
{ \
|
|
if ((result & U_PLUGIN_HANDLER_ERROR) != 0) \
|
|
{ \
|
|
fmt = ((result & U_PLUGIN_HANDLER_FINISHED) != 0 \
|
|
? 0 \
|
|
: "%sWARNING: "#xxx" phase of plugin %.*s failed"); \
|
|
} \
|
|
else \
|
|
{ \
|
|
fmt = (U_ClientImage_parallelization == 2 || \
|
|
(result & U_PLUGIN_HANDLER_PROCESSED) == 0 \
|
|
? 0 \
|
|
: "%s"#xxx" phase of plugin %.*s success"); \
|
|
} \
|
|
\
|
|
if (fmt) ULog::log(fmt,mod_name[0],U_STRING_TO_TRACE(name));\
|
|
} \
|
|
\
|
|
mod_name[0][0] = '\0'; \
|
|
} \
|
|
\
|
|
if ((result & U_PLUGIN_HANDLER_GO_ON) == 0) U_RETURN(result); \
|
|
\
|
|
if (i == 0) U_RETURN(U_PLUGIN_HANDLER_FINISHED); \
|
|
} \
|
|
while (true); \
|
|
}
|
|
#endif
|
|
|
|
// Server-wide hooks
|
|
U_PLUGIN_HANDLER_REVERSE(Init) // NB: we call handlerInit() in reverse order respect to config var PLUGIN...
|
|
U_PLUGIN_HANDLER_REVERSE(Run) // NB: we call handlerRun() in reverse order respect to config var PLUGIN...
|
|
U_PLUGIN_HANDLER_REVERSE(Fork) // NB: we call handlerFork() in reverse order respect to config var PLUGIN...
|
|
U_PLUGIN_HANDLER_REVERSE(Stop) // NB: we call handlerStop() in reverse order respect to config var PLUGIN...
|
|
// Connection-wide hooks
|
|
U_PLUGIN_HANDLER_REVERSE(READ) // NB: we call handlerREAD() in reverse order respect to config var PLUGIN...
|
|
// SigHUP hook
|
|
U_PLUGIN_HANDLER_REVERSE(SigHUP) // NB: we call handlerSigHUP() in reverse order respect to config var PLUGIN...
|
|
|
|
void UServer_Base::init()
|
|
{
|
|
U_TRACE(1, "UServer_Base::init()")
|
|
|
|
U_INTERNAL_ASSERT_POINTER(socket)
|
|
|
|
#ifdef USE_LIBSSL
|
|
if (bssl)
|
|
{
|
|
U_ASSERT(((USSLSocket*)socket)->isSSL())
|
|
|
|
if (cfg) ((USSLSocket*)socket)->ciphersuite_model = cfg->readLong(U_CONSTANT_TO_PARAM("CIPHER_SUITE"));
|
|
|
|
// Load our certificate
|
|
|
|
U_INTERNAL_ASSERT( dh_file->isNullTerminated())
|
|
U_INTERNAL_ASSERT( ca_file->isNullTerminated())
|
|
U_INTERNAL_ASSERT( ca_path->isNullTerminated())
|
|
U_INTERNAL_ASSERT( key_file->isNullTerminated())
|
|
U_INTERNAL_ASSERT( password->isNullTerminated())
|
|
U_INTERNAL_ASSERT(cert_file->isNullTerminated())
|
|
|
|
if (((USSLSocket*)socket)->setContext( dh_file->data(), cert_file->data(), key_file->data(),
|
|
password->data(), ca_file->data(), ca_path->data(), verify_mode) == false)
|
|
{
|
|
U_ERROR("SSL: server setContext() failed");
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (bipc)
|
|
{
|
|
U_ASSERT(socket->isIPC())
|
|
|
|
# ifdef _MSWINDOWS_
|
|
U_ERROR("Sorry, I was compiled on Windows so I can't accept SOCKET_NAME");
|
|
# else
|
|
if (*name_sock) UUnixSocket::setPath(name_sock->data());
|
|
|
|
if (UUnixSocket::path == 0) U_ERROR("UNIX domain socket is not bound to a file system pathname");
|
|
# endif
|
|
}
|
|
}
|
|
|
|
if (socket->setServer(port, server) == false)
|
|
{
|
|
UString x = (server ? *server : U_STRING_FROM_CONSTANT("*"));
|
|
|
|
U_ERROR("Run as server with local address '%.*s:%u' failed", U_STRING_TO_TRACE(x), port);
|
|
}
|
|
|
|
U_SRV_LOG("TCP SO_REUSEPORT status is: %susing", (USocket::tcp_reuseport ? "" : "NOT "));
|
|
|
|
// get name host
|
|
|
|
host = U_NEW(UString(server ? *server : USocketExt::getNodeName()));
|
|
|
|
if (port != U_DEFAULT_PORT)
|
|
{
|
|
host->push_back(':');
|
|
|
|
UStringExt::appendNumber32(*host, port);
|
|
}
|
|
|
|
U_SRV_LOG("HOST registered as: %.*s", U_STRING_TO_TRACE(*host));
|
|
|
|
// get IP address host (default source)
|
|
|
|
if (server &&
|
|
IP_address->empty() &&
|
|
u_isIPAddr(UClientImage_Base::bIPv6, U_STRING_TO_PARAM(*server)))
|
|
{
|
|
*IP_address = *server;
|
|
}
|
|
|
|
#ifdef _MSWINDOWS_
|
|
if (IP_address->empty() ||
|
|
socket->setHostName(*IP_address) == false)
|
|
{
|
|
U_ERROR("On windows we need a valid IP_ADDRESS value on configuration file");
|
|
}
|
|
#else
|
|
/**
|
|
* The above code does NOT make a connection or send any packets (to 64.233.187.99 which is google).
|
|
* Since UDP is a stateless protocol connect() merely makes a system call which figures out how to
|
|
* route the packets based on the address and what interface (and therefore IP address) it should
|
|
* bind to. Returns an array containing the family (AF_INET), local port, and local address (which
|
|
* is what we want) of the socket
|
|
*/
|
|
|
|
UUDPSocket cClientSocket(UClientImage_Base::bIPv6);
|
|
|
|
if (cClientSocket.connectServer(U_STRING_FROM_CONSTANT("8.8.8.8"), 1001))
|
|
{
|
|
socket->setLocal(cClientSocket.cLocalAddress);
|
|
|
|
UString ip = UString(socket->getLocalInfo());
|
|
|
|
if ( IP_address->empty()) *IP_address = ip;
|
|
else if (*IP_address != ip)
|
|
{
|
|
U_SRV_LOG("WARNING: SERVER IP ADDRESS from configuration : %.*S differ from system interface: %.*S", U_STRING_TO_TRACE(*IP_address), U_STRING_TO_TRACE(ip));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifndef _MSWINDOWS_
|
|
if (bipc == false)
|
|
#endif
|
|
{
|
|
struct in_addr ia;
|
|
|
|
if (inet_aton(IP_address->c_str(), &ia) == 0) U_ERROR("IP_ADDRESS conversion fail");
|
|
|
|
socket->setAddress(&ia);
|
|
|
|
public_address = (socket->cLocalAddress.isPrivate() == false);
|
|
|
|
U_SRV_LOG("SERVER IP ADDRESS registered as: %.*s (%s)", U_STRING_TO_TRACE(*IP_address), (public_address ? "public" : "private"));
|
|
|
|
#ifndef _MSWINDOWS_
|
|
u_need_root(false);
|
|
|
|
USocket::tcp_autocorking = UFile::getSysParam("/proc/sys/net/ipv4/tcp_autocorking");
|
|
|
|
/**
|
|
* timeout_timewait parameter: Determines the time that must elapse before TCP/IP can release a closed connection
|
|
* and reuse its resources. This interval between closure and release is known as the TIME_WAIT state or twice the
|
|
* maximum segment lifetime (2MSL) state. During this time, reopening the connection to the client and server cost
|
|
* less than establishing a new connection. By reducing the value of this entry, TCP/IP can release closed connections
|
|
* faster, providing more resources for new connections. Adjust this parameter if the running application requires rapid
|
|
* release, the creation of new connections, and a low throughput due to many connections sitting in the TIME_WAIT state
|
|
*/
|
|
|
|
tcp_fin_timeout = UFile::getSysParam("/proc/sys/net/ipv4/tcp_fin_timeout");
|
|
if (tcp_fin_timeout > 30) tcp_fin_timeout = UFile::setSysParam("/proc/sys/net/ipv4/tcp_fin_timeout", 30, true);
|
|
|
|
/**
|
|
* sysctl_somaxconn (SOMAXCONN: 128) specifies the maximum number of sockets in state SYN_RECV per listen socket queue.
|
|
* At listen(2) time the backlog is adjusted to this limit if bigger then that.
|
|
*
|
|
* sysctl_max_syn_backlog on the other hand is dynamically adjusted, depending on the memory characteristic of the system.
|
|
* Default is 256, 128 for small systems and up to 1024 for bigger systems.
|
|
*
|
|
* The system limits (somaxconn & tcp_max_syn_backlog) specify a _maximum_, the user cannot exceed this limit with listen(2).
|
|
* The backlog argument for listen on the other hand specify a _minimum_
|
|
*/
|
|
|
|
if (USocket::iBackLog == 1)
|
|
{
|
|
// sysctl_tcp_abort_on_overflow when its on, new connections are reset once the backlog is exhausted
|
|
|
|
tcp_abort_on_overflow = UFile::setSysParam("/proc/sys/net/ipv4/tcp_abort_on_overflow", 1, true);
|
|
}
|
|
else if (USocket::iBackLog >= SOMAXCONN)
|
|
{
|
|
int value = USocket::iBackLog * 2;
|
|
|
|
// NB: take a look at `netstat -s | grep overflowed`
|
|
|
|
sysctl_somaxconn = UFile::setSysParam("/proc/sys/net/core/somaxconn", value);
|
|
sysctl_max_syn_backlog = UFile::setSysParam("/proc/sys/net/ipv4/tcp_max_syn_backlog", value * 2);
|
|
}
|
|
|
|
U_INTERNAL_DUMP("sysctl_somaxconn = %d tcp_abort_on_overflow = %b sysctl_max_syn_backlog = %d USocket::tcp_autocorking = %d",
|
|
sysctl_somaxconn, tcp_abort_on_overflow, sysctl_max_syn_backlog, USocket::tcp_autocorking)
|
|
#endif
|
|
}
|
|
|
|
if (str_preforked_num_kids)
|
|
{
|
|
preforked_num_kids = str_preforked_num_kids->strtol();
|
|
|
|
# ifdef U_SERVER_CAPTIVE_PORTAL
|
|
if (str_preforked_num_kids->c_char(0) == '0') monitoring_process = true;
|
|
# endif
|
|
|
|
delete str_preforked_num_kids;
|
|
str_preforked_num_kids = 0;
|
|
|
|
# if !defined(ENABLE_THREAD) || !defined(HAVE_EPOLL_WAIT) || !defined(U_SERVER_THREAD_APPROACH_SUPPORT) || defined(USE_LIBEVENT)
|
|
if (preforked_num_kids == -1)
|
|
{
|
|
U_WARNING("Sorry, I was compiled without server thread approach so I can't accept PREFORK_CHILD == -1");
|
|
|
|
preforked_num_kids = 2;
|
|
}
|
|
# endif
|
|
}
|
|
#ifndef _MSWINDOWS_
|
|
else
|
|
{
|
|
preforked_num_kids = u_get_num_cpu();
|
|
|
|
U_INTERNAL_DUMP("num_cpu = %d", preforked_num_kids)
|
|
|
|
if (preforked_num_kids < 2) preforked_num_kids = 2;
|
|
}
|
|
#endif
|
|
|
|
#ifndef U_CLASSIC_SUPPORT
|
|
if (isClassic())
|
|
{
|
|
U_WARNING("Sorry, I was compiled without server classic model support so I can't accept PREFORK_CHILD == 1");
|
|
|
|
preforked_num_kids = 2;
|
|
}
|
|
#endif
|
|
|
|
#ifdef _MSWINDOWS_
|
|
if (preforked_num_kids > 0)
|
|
{
|
|
U_WARNING("Sorry, I was compiled on Windows so I can't accept PREFORK_CHILD > 0");
|
|
|
|
preforked_num_kids = 0;
|
|
}
|
|
#endif
|
|
|
|
if (preforked_num_kids > 1)
|
|
{
|
|
monitoring_process = true;
|
|
|
|
if (num_client_for_parallelization == 0) num_client_for_parallelization = preforked_num_kids;
|
|
}
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(proc, 0)
|
|
|
|
proc = U_NEW(UProcess);
|
|
|
|
U_INTERNAL_ASSERT_POINTER(proc)
|
|
|
|
proc->setProcessGroup();
|
|
|
|
UClientImage_Base::init();
|
|
|
|
USocket::accept4_flags = SOCK_CLOEXEC | SOCK_NONBLOCK;
|
|
|
|
#ifdef U_LOG_ENABLE
|
|
uint32_t log_rotate_size = 0;
|
|
|
|
# ifdef USE_LIBZ
|
|
if (isLog())
|
|
{
|
|
// The zlib documentation states that destination buffer size must be at least 0.1% larger than avail_in plus 12 bytes
|
|
|
|
log_rotate_size =
|
|
shared_data_add = log->UFile::st_size + (log->UFile::st_size / 10) + 12U;
|
|
}
|
|
# endif
|
|
|
|
U_INTERNAL_DUMP("log_rotate_size = %u", log_rotate_size)
|
|
#endif
|
|
|
|
// init plugin modules, must run after the setting for shared log
|
|
|
|
if (pluginsHandlerInit() != U_PLUGIN_HANDLER_FINISHED) U_ERROR("Plugins stage init failed");
|
|
|
|
// manage shared data...
|
|
|
|
U_INTERNAL_DUMP("shared_data_add = %u", shared_data_add)
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(ptr_shared_data, 0)
|
|
|
|
map_size = sizeof(shared_data) + shared_data_add;
|
|
ptr_shared_data = (shared_data*) UFile::mmap(&map_size);
|
|
|
|
U_INTERNAL_ASSERT_POINTER(ptr_shared_data)
|
|
U_INTERNAL_ASSERT_DIFFERS(ptr_shared_data, MAP_FAILED)
|
|
|
|
#ifdef ENABLE_THREAD
|
|
bool bpthread_time = (preforked_num_kids >= 8); // intuitive heuristic...
|
|
#else
|
|
bool bpthread_time = false;
|
|
#endif
|
|
|
|
U_INTERNAL_DUMP("bpthread_time = %b", bpthread_time)
|
|
|
|
#ifdef U_LOG_ENABLE
|
|
if (isLog() == false)
|
|
#endif
|
|
ULog::initStaticDate();
|
|
|
|
if (bpthread_time == false) ptr_static_date = ULog::ptr_static_date;
|
|
#ifdef ENABLE_THREAD
|
|
else
|
|
{
|
|
ptr_static_date = &(ptr_shared_data->static_date);
|
|
|
|
U_INTERNAL_ASSERT_POINTER( ULog::ptr_static_date)
|
|
U_INTERNAL_ASSERT_EQUALS((void*)u_now, (void*)ULog::ptr_static_date)
|
|
|
|
U_MEMCPY(ptr_static_date, ULog::ptr_static_date, sizeof(ULog::static_date));
|
|
|
|
u_now = &(ptr_static_date->_timeval);
|
|
ULog::iov_vec[0].iov_base = (caddr_t)ptr_static_date->date1;
|
|
|
|
U_FREE_TYPE(ULog::ptr_static_date, ULog::static_date);
|
|
|
|
ULog::ptr_static_date = ptr_static_date;
|
|
}
|
|
#endif
|
|
|
|
U_INTERNAL_ASSERT_EQUALS((void*)u_now, (void*)ptr_static_date)
|
|
U_INTERNAL_ASSERT_EQUALS((void*)u_now, (void*)ULog::ptr_static_date)
|
|
|
|
#if defined(ENABLE_THREAD) && !defined(_MSWINDOWS_)
|
|
// NB: we block SIGHUP and SIGTERM; the threads created will inherit a copy of the signal mask...
|
|
# ifdef sigemptyset
|
|
sigemptyset(&mask);
|
|
# else
|
|
(void) U_SYSCALL(sigemptyset, "%p", &mask);
|
|
# endif
|
|
|
|
# ifdef sigaddset
|
|
sigaddset(&mask, SIGHUP);
|
|
sigaddset(&mask, SIGTERM);
|
|
# else
|
|
(void) U_SYSCALL(sigaddset, "%p,%d", &mask, SIGHUP);
|
|
(void) U_SYSCALL(sigaddset, "%p,%d", &mask, SIGTERM);
|
|
# endif
|
|
|
|
(void) U_SYSCALL(pthread_sigmask, "%d,%p,%p", SIG_BLOCK, &mask, 0);
|
|
#endif
|
|
|
|
flag_loop = true; // NB: UTimeThread loop depend on this...
|
|
|
|
#ifdef ENABLE_THREAD
|
|
if (bpthread_time)
|
|
{
|
|
U_INTERNAL_ASSERT_EQUALS(u_pthread_time, 0)
|
|
|
|
U_NEW_ULIB_OBJECT(u_pthread_time, UTimeThread);
|
|
|
|
U_INTERNAL_DUMP("u_pthread_time = %p", u_pthread_time)
|
|
|
|
((UTimeThread*)u_pthread_time)->start(50);
|
|
}
|
|
#endif
|
|
|
|
#ifdef U_LOG_ENABLE
|
|
if (isLog())
|
|
{
|
|
// NB: if log is mapped must be always shared cause of possibility of fork() by parallelization
|
|
|
|
if (log->isMemoryMapped()) log->setShared(U_LOG_DATA_SHARED, log_rotate_size);
|
|
|
|
U_SRV_LOG("Mapped %u bytes (%u KB) of shared memory for %d preforked process",
|
|
sizeof(shared_data) + shared_data_add, map_size / 1024, preforked_num_kids);
|
|
}
|
|
#endif
|
|
|
|
last_event = u_now->tv_sec;
|
|
|
|
#ifdef U_LOG_ENABLE
|
|
U_INTERNAL_ASSERT_EQUALS(U_TOT_CONNECTION, 0)
|
|
#endif
|
|
|
|
if (timeoutMS > 0) ptime = U_NEW(UTimeoutConnection);
|
|
|
|
// ---------------------------------------------------------------------------------------------------------
|
|
// init notifier event manager
|
|
// ---------------------------------------------------------------------------------------------------------
|
|
// NB: in the classic model we don't need to be notified for request of connection (loop: accept-fork)
|
|
// and the forked child don't accept new client, but maybe we need anyway the event manager because
|
|
// the forked child must feel the possibly timeout for request from the new client...
|
|
// ---------------------------------------------------------------------------------------------------------
|
|
|
|
if (preforked_num_kids != -1)
|
|
{
|
|
if (timeoutMS > 0 ||
|
|
isClassic() == false)
|
|
{
|
|
UNotifier::min_connection = 1;
|
|
}
|
|
|
|
if (handler_inotify) UNotifier::min_connection++;
|
|
}
|
|
|
|
UNotifier::max_connection = (UNotifier::max_connection ? UNotifier::max_connection : USocket::iBackLog / 2) + (UNotifier::num_connection = UNotifier::min_connection);
|
|
|
|
U_INTERNAL_DUMP("UNotifier::max_connection = %u", UNotifier::max_connection)
|
|
|
|
pthis->preallocate();
|
|
|
|
USocket::server_flags |= O_RDWR | O_CLOEXEC;
|
|
|
|
if (UNotifier::min_connection)
|
|
{
|
|
U_INTERNAL_ASSERT_DIFFERS(preforked_num_kids, -1)
|
|
|
|
/**
|
|
* There may not always be a connection waiting after a SIGIO is delivered or select(2) or poll(2) return
|
|
* a readability event because the connection might have been removed by an asynchronous network error or
|
|
* another thread before accept() is called. If this happens then the call will block waiting for the next
|
|
* connection to arrive. To ensure that accept() never blocks, the passed socket sockfd needs to have the
|
|
* O_NONBLOCK flag set (see socket(7))
|
|
*/
|
|
|
|
USocket::server_flags |= O_NONBLOCK;
|
|
|
|
if (timeoutMS > 0 ||
|
|
isClassic() == false)
|
|
{
|
|
binsert = true; // NB: we ask to be notified for request of connection (=> accept)
|
|
|
|
/**
|
|
* Edge trigger (EPOLLET) simply means (unless you've used EPOLLONESHOT) that you'll get 1 event when something
|
|
* enters the (kernel) buffer. Thus, if you get 1 EPOLLIN event and do nothing about it, you'll get another
|
|
* EPOLLIN the next time some data arrives on that descriptor - if no new data arrives, you will not get an
|
|
* event though, even if you didn't read any data as indicated by the first event. Well, to put it succinctly,
|
|
* EPOLLONESHOT just means that if you don't read the data you're supposed to read, they will be discarded.
|
|
* Normally, you'd be notified with an event for the same data if you don't read them. With EPOLLONESHOT, however,
|
|
* not reading the data is perfectly legal and they will be just ignored. Hence, no further events will be generated.
|
|
* -------------------------------------------------------------------------------------------------------------------
|
|
* The suggested way to use epoll as an edge-triggered (EPOLLET) interface is as follows:
|
|
*
|
|
* 1) with nonblocking file descriptors
|
|
* 2) by waiting for an event only after read(2) or write(2) return EAGAIN.
|
|
* -------------------------------------------------------------------------------------------------------------------
|
|
* Edge-triggered semantics allow a more efficient internal implementation than level-triggered semantics.
|
|
*
|
|
* see: https://raw.githubusercontent.com/dankamongmen/libtorque/master/doc/mteventqueues
|
|
*/
|
|
|
|
# if defined(HAVE_EPOLL_WAIT) && !defined(USE_LIBEVENT) && !defined(U_SERVER_CAPTIVE_PORTAL)
|
|
if (bssl == false) UNotifier::add_mask |= EPOLLET; // NB: we try to manage optimally a burst of new connections...
|
|
# endif
|
|
}
|
|
}
|
|
}
|
|
|
|
bool UServer_Base::addLog(UFile* _log, int flags)
|
|
{
|
|
U_TRACE(0, "UServer_Base::addLog(%p,%d)", _log, flags)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(vlog)
|
|
|
|
if (_log->creat(flags, PERM_FILE))
|
|
{
|
|
file_LOG* item = U_NEW(file_LOG);
|
|
|
|
item->LOG = _log;
|
|
item->flags = flags;
|
|
|
|
vlog->push_back(item);
|
|
|
|
U_RETURN(true);
|
|
}
|
|
|
|
U_RETURN(false);
|
|
}
|
|
|
|
void UServer_Base::reopenLog()
|
|
{
|
|
U_TRACE(0, "UServer_Base::reopenLog()")
|
|
|
|
file_LOG* item;
|
|
|
|
for (uint32_t i = 0, n = vlog->size(); i < n; ++i)
|
|
{
|
|
item = (*vlog)[i];
|
|
|
|
item->LOG->reopen(item->flags);
|
|
}
|
|
}
|
|
|
|
U_NO_EXPORT void UServer_Base::logMemUsage(const char* signame)
|
|
{
|
|
U_TRACE(0, "UServer_Base::logMemUsage(%S)", signame)
|
|
|
|
U_INTERNAL_ASSERT(isLog())
|
|
|
|
#ifdef U_LOG_ENABLE
|
|
unsigned long vsz, rss;
|
|
|
|
u_get_memusage(&vsz, &rss);
|
|
|
|
ULog::log("%s (Interrupt): "
|
|
"address space usage: %.2f MBytes - "
|
|
"rss usage: %.2f MBytes", signame,
|
|
(double)vsz / (1024.0 * 1024.0),
|
|
(double)rss / (1024.0 * 1024.0));
|
|
#endif
|
|
}
|
|
|
|
RETSIGTYPE UServer_Base::handlerForSigCHLD(int signo)
|
|
{
|
|
U_TRACE(0, "[SIGCHLD] UServer_Base::handlerForSigCHLD(%d)", signo)
|
|
|
|
if (proc->parent()) proc->wait();
|
|
}
|
|
|
|
U_NO_EXPORT void UServer_Base::manageSigHUP()
|
|
{
|
|
U_TRACE(0, "UServer_Base::manageSigHUP()")
|
|
|
|
U_INTERNAL_ASSERT_POINTER(proc)
|
|
|
|
(void) proc->waitAll(1);
|
|
|
|
if (pluginsHandlerSigHUP() != U_PLUGIN_HANDLER_FINISHED) U_WARNING("Plugins stage SigHUP failed...");
|
|
}
|
|
|
|
RETSIGTYPE UServer_Base::handlerForSigHUP(int signo)
|
|
{
|
|
U_TRACE(0, "[SIGHUP] UServer_Base::handlerForSigHUP(%d)", signo)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(pthis)
|
|
|
|
U_INTERNAL_ASSERT(proc->parent())
|
|
|
|
// NB: for logrotate...
|
|
|
|
#ifdef U_LOG_ENABLE
|
|
if (isLog())
|
|
{
|
|
logMemUsage("SIGHUP");
|
|
|
|
log->reopen();
|
|
}
|
|
#endif
|
|
|
|
if (isOtherLog()) reopenLog();
|
|
|
|
(void) U_SYSCALL(gettimeofday, "%p,%p", u_now, 0);
|
|
|
|
#ifdef ENABLE_THREAD
|
|
if (u_pthread_time) ((UTimeThread*)u_pthread_time)->suspend();
|
|
#endif
|
|
|
|
pthis->handlerSignal(); // manage before regenering preforked pool of children...
|
|
|
|
// NB: we can't use UInterrupt::erase() because it restore the old action (UInterrupt::init)...
|
|
|
|
UInterrupt::setHandlerForSignal(SIGTERM, (sighandler_t)SIG_IGN);
|
|
|
|
UProcess::kill(0, SIGTERM); // SIGTERM is sent to every process in the process group of the calling process...
|
|
|
|
#if defined(USE_LIBEVENT)
|
|
UInterrupt::setHandlerForSignal(SIGTERM, (sighandler_t)UServer_Base::handlerForSigTERM); // sync signal
|
|
#else
|
|
UInterrupt::insert(SIGTERM, (sighandler_t)UServer_Base::handlerForSigTERM); // async signal
|
|
#endif
|
|
|
|
#ifdef ENABLE_THREAD
|
|
if (u_pthread_time)
|
|
{
|
|
# ifdef DEBUG
|
|
(void) U_SYSCALL(gettimeofday, "%p,%p", u_now, 0);
|
|
|
|
((UTimeThread*)u_pthread_time)->before.set(*u_now);
|
|
# endif
|
|
((UTimeThread*)u_pthread_time)->watch_counter = 0;
|
|
|
|
((UTimeThread*)u_pthread_time)->resume();
|
|
}
|
|
#endif
|
|
|
|
#ifdef U_LOG_ENABLE
|
|
U_TOT_CONNECTION = 0;
|
|
#endif
|
|
|
|
if (preforked_num_kids > 1) rkids = 0;
|
|
else manageSigHUP();
|
|
}
|
|
|
|
RETSIGTYPE UServer_Base::handlerForSigTERM(int signo)
|
|
{
|
|
U_TRACE(0, "[SIGTERM] UServer_Base::handlerForSigTERM(%d)", signo)
|
|
|
|
flag_loop = false;
|
|
flag_sigterm = true;
|
|
|
|
U_INTERNAL_ASSERT_POINTER(proc)
|
|
|
|
if (proc->parent())
|
|
{
|
|
# ifdef ENABLE_THREAD
|
|
if (u_pthread_time) ((UTimeThread*)u_pthread_time)->suspend();
|
|
# endif
|
|
|
|
// NB: we can't use UInterrupt::erase() because it restore the old action (UInterrupt::init)...
|
|
|
|
UInterrupt::setHandlerForSignal(SIGTERM, (sighandler_t)SIG_IGN);
|
|
|
|
UProcess::kill(0, SIGTERM); // SIGTERM is sent to every process in the process group of the calling process...
|
|
}
|
|
else
|
|
{
|
|
# ifdef USE_LIBEVENT
|
|
(void) UDispatcher::exit(0);
|
|
# elif !defined(USE_RUBY)
|
|
UInterrupt::erase(SIGTERM); // async signal
|
|
# endif
|
|
|
|
# if defined(U_STDCPP_ENABLE) && defined(DEBUG)
|
|
if (U_SYSCALL(getenv, "%S", "UMEMUSAGE"))
|
|
{
|
|
uint32_t len;
|
|
char buffer[4096];
|
|
unsigned long vsz, rss;
|
|
|
|
u_get_memusage(&vsz, &rss);
|
|
|
|
len = u__snprintf(buffer, sizeof(buffer),
|
|
"SIGTERM (Interrupt): "
|
|
"address space usage: %.2f MBytes - "
|
|
"rss usage: %.2f MBytes\n"
|
|
"max_depth = %u wakeup_for_nothing = %u\n",
|
|
(double)vsz / (1024.0 * 1024.0),
|
|
(double)rss / (1024.0 * 1024.0), max_depth - - UNotifier::min_connection, wakeup_for_nothing);
|
|
|
|
ostrstream os(buffer + len, sizeof(buffer) - len);
|
|
|
|
UMemoryPool::printInfo(os);
|
|
|
|
len += os.pcount();
|
|
|
|
U_INTERNAL_ASSERT_MINOR(len, sizeof(buffer))
|
|
|
|
(void) UFile::writeToTmp(buffer, len, false, "%N.memusage.%P", 0);
|
|
}
|
|
# endif
|
|
|
|
if (preforked_num_kids)
|
|
{
|
|
# if defined(ENABLE_THREAD) && defined(HAVE_EPOLL_WAIT) && !defined(USE_LIBEVENT) && defined(U_SERVER_THREAD_APPROACH_SUPPORT)
|
|
if (preforked_num_kids == -1) ((UThread*)UNotifier::pthread)->suspend();
|
|
# if defined(HAVE_SYS_SYSCALL_H) && defined(DEBUG)
|
|
if (u_plock) (void) pthread_mutex_unlock((pthread_mutex_t*)u_plock);
|
|
# endif
|
|
# endif
|
|
|
|
# ifdef U_LOG_ENABLE
|
|
if (isLog()) logMemUsage("SIGTERM");
|
|
# endif
|
|
|
|
U_EXIT(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
int UServer_Base::handlerRead() // This method is called to accept a new connection on the server socket
|
|
{
|
|
U_TRACE(1, "UServer_Base::handlerRead()")
|
|
|
|
U_INTERNAL_ASSERT_POINTER(ptr_shared_data)
|
|
|
|
int cround = 0;
|
|
USocket* csocket;
|
|
|
|
#ifdef DEBUG
|
|
client_address_len = 0;
|
|
#endif
|
|
|
|
loop:
|
|
U_INTERNAL_ASSERT_MINOR(pClientIndex, eClientImage)
|
|
U_INTERNAL_ASSERT_DIFFERS(U_ClientImage_parallelization, 1) // 1 => child of parallelization
|
|
|
|
U_INTERNAL_DUMP("----------------------------------------", 0)
|
|
U_INTERNAL_DUMP("vClientImage[%d].last_event = %#3D", (pClientIndex - vClientImage),
|
|
pClientIndex->last_event)
|
|
U_INTERNAL_DUMP("vClientImage[%u].sfd = %d", (pClientIndex - vClientImage),
|
|
pClientIndex->sfd)
|
|
U_INTERNAL_DUMP("vClientImage[%u].UEventFd::fd = %d", (pClientIndex - vClientImage),
|
|
pClientIndex->UEventFd::fd)
|
|
U_INTERNAL_DUMP("vClientImage[%u].socket = %p", (pClientIndex - vClientImage),
|
|
pClientIndex->socket)
|
|
U_INTERNAL_DUMP("vClientImage[%d].socket->flags = %d %B", (pClientIndex - vClientImage),
|
|
pClientIndex->socket->flags,
|
|
pClientIndex->socket->flags)
|
|
U_INTERNAL_DUMP("----------------------------------------", 0)
|
|
|
|
csocket = pClientIndex->socket;
|
|
|
|
if (csocket->isOpen()) // busy
|
|
{
|
|
if (timeoutMS > 0) // NB: we check if the connection is idle...
|
|
{
|
|
U_INTERNAL_ASSERT_POINTER(ptime)
|
|
|
|
U_gettimeofday; // NB: optimization if it is enough a time resolution of one second...
|
|
|
|
if ((u_now->tv_sec - pClientIndex->last_event) >= ptime->UTimeVal::tv_sec &&
|
|
handlerTimeoutConnection(0))
|
|
{
|
|
UNotifier::erase((UEventFd*)pClientIndex);
|
|
|
|
goto try_accept;
|
|
}
|
|
}
|
|
|
|
#if !defined(U_SERVER_CAPTIVE_PORTAL) && (defined(U_ACL_SUPPORT) || defined(U_RFC1918_SUPPORT))
|
|
try_next:
|
|
#endif
|
|
if (++pClientIndex >= eClientImage)
|
|
{
|
|
U_INTERNAL_ASSERT_POINTER(vClientImage)
|
|
|
|
if (++cround >= 2) U_ERROR("out of space on client image: preallocation(%u) - connection(%u)", UNotifier::max_connection, UNotifier::num_connection - UNotifier::min_connection);
|
|
|
|
pClientIndex = vClientImage;
|
|
}
|
|
|
|
goto loop;
|
|
}
|
|
|
|
try_accept:
|
|
U_INTERNAL_ASSERT(csocket->isClosed())
|
|
U_INTERNAL_ASSERT_DIFFERS(U_ClientImage_parallelization, 1) // 1 => child of parallelization
|
|
|
|
if (socket->acceptClient(csocket) == false)
|
|
{
|
|
U_INTERNAL_DUMP("flag_loop = %b csocket->iState = %d", flag_loop, csocket->iState)
|
|
|
|
# ifdef DEBUG
|
|
if (client_address_len == 0 &&
|
|
csocket->iState == -EAGAIN)
|
|
{
|
|
++wakeup_for_nothing;
|
|
}
|
|
# endif
|
|
# ifdef U_LOG_ENABLE
|
|
if (isLog() &&
|
|
flag_loop && // NB: we check to avoid SIGTERM event...
|
|
csocket->iState != -EINTR && // NB: we check to avoid log spurious EINTR on accept() by timer...
|
|
csocket->iState != -EAGAIN)
|
|
{
|
|
csocket->setMsgError();
|
|
|
|
if (u_buffer_len)
|
|
{
|
|
ULog::log("WARNING: accept new client failed %.*S", u_buffer_len, u_buffer);
|
|
|
|
u_buffer_len = 0;
|
|
}
|
|
}
|
|
# endif
|
|
|
|
U_RETURN(U_NOTIFIER_OK);
|
|
}
|
|
|
|
U_INTERNAL_ASSERT(csocket->isConnected())
|
|
|
|
client_address = UIPAddress::resolveStrAddress(iAddressType, csocket->cRemoteAddress.pcAddress.p, csocket->cRemoteAddress.pcStrAddress);
|
|
client_address_len = u__strlen(client_address, __PRETTY_FUNCTION__);
|
|
|
|
U_INTERNAL_DUMP("client_address = %.*S", U_CLIENT_ADDRESS_TO_TRACE)
|
|
|
|
#ifdef U_ACL_SUPPORT
|
|
if (vallow_IP &&
|
|
UIPAllow::isAllowed(csocket->remoteIPAddress().getInAddr(), *vallow_IP) == false)
|
|
{
|
|
csocket->close();
|
|
|
|
// Instructs server to accept connections from the IP address IPADDR. A CIDR mask length can be supplied optionally after
|
|
// a trailing slash, e.g. 192.168.0.0/24, in which case addresses that match in the most significant MASK bits will be allowed.
|
|
// If no options are specified, all clients are allowed. Unauthorized connections are rejected by closing the TCP connection
|
|
// immediately. A warning is logged on the server but nothing is sent to the client.
|
|
|
|
U_SRV_LOG("WARNING: new client connected from %.*S, connection denied by Access Control List", U_CLIENT_ADDRESS_TO_TRACE);
|
|
|
|
# ifdef USE_LIBSSL
|
|
if (bssl == false)
|
|
# endif
|
|
{
|
|
# if defined(ENABLE_THREAD) && defined(HAVE_EPOLL_WAIT) && !defined(USE_LIBEVENT)
|
|
if (preforked_num_kids != -1)
|
|
# endif
|
|
{
|
|
U_INTERNAL_ASSERT((USocket::server_flags & O_NONBLOCK) != 0)
|
|
#if !defined(U_SERVER_CAPTIVE_PORTAL) && !defined(_MSWINDOWS_)
|
|
U_INTERNAL_ASSERT(UNotifier::add_mask & EPOLLET)
|
|
|
|
goto try_next; // NB: we try to manage optimally a burst of new connections...
|
|
# endif
|
|
}
|
|
}
|
|
|
|
U_RETURN(U_NOTIFIER_OK);
|
|
}
|
|
#endif
|
|
|
|
#ifdef U_RFC1918_SUPPORT
|
|
if (public_address &&
|
|
enable_rfc1918_filter &&
|
|
csocket->remoteIPAddress().isPrivate() &&
|
|
(vallow_IP_prv == 0 ||
|
|
UIPAllow::isAllowed(csocket->remoteIPAddress().getInAddr(), *vallow_IP_prv) == false))
|
|
{
|
|
csocket->close();
|
|
|
|
U_SRV_LOG("WARNING: new client connected from %.*S, connection denied by RFC1918 filtering "
|
|
"(reject request from private IP to public server address)", U_CLIENT_ADDRESS_TO_TRACE);
|
|
|
|
# ifdef USE_LIBSSL
|
|
if (bssl == false)
|
|
# endif
|
|
{
|
|
# if defined(ENABLE_THREAD) && defined(HAVE_EPOLL_WAIT) && !defined(USE_LIBEVENT)
|
|
if (preforked_num_kids != -1)
|
|
# endif
|
|
{
|
|
U_INTERNAL_ASSERT((USocket::server_flags & O_NONBLOCK) != 0)
|
|
#if !defined(U_SERVER_CAPTIVE_PORTAL) && !defined(_MSWINDOWS_)
|
|
U_INTERNAL_ASSERT(UNotifier::add_mask & EPOLLET)
|
|
|
|
goto try_next; // NB: we try to manage optimally a burst of new connections...
|
|
# endif
|
|
}
|
|
}
|
|
|
|
U_RETURN(U_NOTIFIER_OK);
|
|
}
|
|
#endif
|
|
|
|
#ifdef U_LOG_ENABLE
|
|
U_TOT_CONNECTION++;
|
|
|
|
U_INTERNAL_DUMP("U_TOT_CONNECTION = %u", U_TOT_CONNECTION)
|
|
#endif
|
|
|
|
++UNotifier::num_connection;
|
|
|
|
U_INTERNAL_DUMP("UNotifier::num_connection = %u", UNotifier::num_connection)
|
|
|
|
/**
|
|
* PREFORK_CHILD number of child server processes created at startup:
|
|
*
|
|
* -1 - thread approach (experimental)
|
|
* 0 - serialize, no forking
|
|
* 1 - classic, forking after accept client
|
|
* >1 - pool of process serialize plus monitoring process
|
|
*/
|
|
|
|
#ifdef U_CLASSIC_SUPPORT
|
|
if (isClassic())
|
|
{
|
|
if (proc->fork() &&
|
|
proc->parent())
|
|
{
|
|
int status;
|
|
|
|
csocket->close();
|
|
|
|
U_SRV_LOG("Started new child (pid %d), up to %u children", proc->pid(), UNotifier::num_connection - UNotifier::min_connection);
|
|
|
|
retry: pid = UProcess::waitpid(-1, &status, WNOHANG); // NB: to avoid too much zombie...
|
|
|
|
if (pid > 0)
|
|
{
|
|
char buffer[128];
|
|
|
|
--UNotifier::num_connection;
|
|
|
|
U_SRV_LOG("Child (pid %d) exited with value %d (%s), down to %u children",
|
|
pid, status, UProcess::exitInfo(buffer, status), UNotifier::num_connection - UNotifier::min_connection);
|
|
|
|
goto retry;
|
|
}
|
|
|
|
# ifdef U_LOG_ENABLE
|
|
if (isLog()) ULog::log("Waiting for connection on port %u", port);
|
|
# endif
|
|
|
|
U_RETURN(U_NOTIFIER_OK);
|
|
}
|
|
|
|
if (proc->child())
|
|
{
|
|
UNotifier::init(false);
|
|
|
|
if (timeoutMS > 0) ptime = U_NEW(UTimeoutConnection);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef U_LOG_ENABLE
|
|
if (isLog())
|
|
{
|
|
# ifdef USE_LIBSSL
|
|
if (bssl) pClientIndex->logCertificate();
|
|
# endif
|
|
|
|
USocketExt::setRemoteInfo(csocket, *pClientIndex->logbuf);
|
|
|
|
U_INTERNAL_ASSERT(pClientIndex->logbuf->isNullTerminated())
|
|
|
|
char buffer[32];
|
|
uint32_t len = getNumConnection(buffer);
|
|
|
|
ULog::log("New client connected from %.*s, %.*s clients currently connected", U_STRING_TO_TRACE(*pClientIndex->logbuf), len, buffer);
|
|
|
|
# ifdef U_WELCOME_SUPPORT
|
|
if (msg_welcome) ULog::log("Send welcome message to %.*s", U_STRING_TO_TRACE(*pClientIndex->logbuf));
|
|
# endif
|
|
}
|
|
#endif
|
|
|
|
pClientIndex->UEventFd::fd = csocket->iSockDesc;
|
|
|
|
#ifdef U_WELCOME_SUPPORT
|
|
if (msg_welcome &&
|
|
USocketExt::write(csocket, *msg_welcome, timeoutMS) == false)
|
|
{
|
|
csocket->close();
|
|
|
|
pClientIndex->UClientImage_Base::handlerDelete();
|
|
|
|
goto next;
|
|
}
|
|
#endif
|
|
|
|
#if defined(ENABLE_THREAD) && defined(HAVE_EPOLL_WAIT) && !defined(USE_LIBEVENT) && defined(U_SERVER_THREAD_APPROACH_SUPPORT)
|
|
if (UNotifier::pthread == 0)
|
|
#endif
|
|
{
|
|
if (pClientIndex->handlerRead() == U_NOTIFIER_DELETE)
|
|
{
|
|
if (csocket->isOpen())
|
|
{
|
|
csocket->iState = USocket::CONNECT;
|
|
|
|
csocket->close();
|
|
}
|
|
|
|
pClientIndex->UClientImage_Base::handlerDelete();
|
|
|
|
goto next;
|
|
}
|
|
}
|
|
|
|
U_INTERNAL_ASSERT(csocket->isOpen())
|
|
U_INTERNAL_ASSERT_DIFFERS(U_ClientImage_parallelization, 1) // 1 => child of parallelization
|
|
|
|
#if !defined(HAVE_EPOLL_WAIT) && !defined(USE_LIBEVENT) && defined(_MSWINDOWS_)
|
|
if (csocket->iSockDesc >= FD_SETSIZE)
|
|
{
|
|
csocket->close();
|
|
|
|
--UNotifier::num_connection;
|
|
|
|
U_SRV_LOG("WARNING: new client connected from %.*S, connection denied by FD_SETSIZE(%u)", U_CLIENT_ADDRESS_TO_TRACE, FD_SETSIZE);
|
|
|
|
U_RETURN(U_NOTIFIER_OK);
|
|
}
|
|
#endif
|
|
|
|
if (UNotifier::num_connection >= UNotifier::max_connection)
|
|
{
|
|
U_SRV_LOG("WARNING: new client connected from %.*S, num_connection(%u) greater than MAX_KEEP_ALIVE(%u)",
|
|
U_CLIENT_ADDRESS_TO_TRACE, UNotifier::num_connection, UNotifier::max_connection - UNotifier::min_connection);
|
|
|
|
# ifdef U_SERVER_CAPTIVE_PORTAL // NB: we check for idle connection in the middle of a burst of new connections (DOS attack)...
|
|
if (timeoutMS > 0)
|
|
{
|
|
U_INTERNAL_ASSERT_EQUALS(preforked_num_kids, 0)
|
|
|
|
U_SYSCALL(gettimeofday, "%p,%p", u_now, 0);
|
|
|
|
last_event = u_now->tv_sec;
|
|
|
|
UNotifier::callForAllEntryDynamic(handlerTimeoutConnection);
|
|
}
|
|
# endif
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if (max_depth < UNotifier::num_connection) max_depth = UNotifier::num_connection;
|
|
#endif
|
|
|
|
pClientIndex->last_event = u_now->tv_sec;
|
|
|
|
UNotifier::insert((UEventFd*)pClientIndex);
|
|
|
|
if (++pClientIndex >= eClientImage)
|
|
{
|
|
U_INTERNAL_ASSERT_POINTER(vClientImage)
|
|
|
|
pClientIndex = vClientImage;
|
|
}
|
|
|
|
next:
|
|
last_event = u_now->tv_sec;
|
|
|
|
#ifdef USE_LIBSSL
|
|
if (bssl == false)
|
|
#endif
|
|
{
|
|
#if defined(ENABLE_THREAD) && defined(HAVE_EPOLL_WAIT) && !defined(USE_LIBEVENT)
|
|
if (preforked_num_kids != -1)
|
|
#endif
|
|
{
|
|
U_INTERNAL_ASSERT((USocket::server_flags & O_NONBLOCK) != 0)
|
|
|
|
#if !defined(U_SERVER_CAPTIVE_PORTAL) && !defined(_MSWINDOWS_)
|
|
U_INTERNAL_ASSERT(UNotifier::add_mask & EPOLLET)
|
|
|
|
cround = 0;
|
|
|
|
goto loop; // NB: we try to manage optimally a burst of new connections...
|
|
#endif
|
|
}
|
|
}
|
|
|
|
U_RETURN(U_NOTIFIER_OK);
|
|
}
|
|
|
|
#ifdef U_LOG_ENABLE
|
|
uint32_t UServer_Base::getNumConnection(char* ptr)
|
|
{
|
|
U_TRACE(0, "UServer_Base::getNumConnection(%p)", ptr)
|
|
|
|
uint32_t len;
|
|
|
|
if (preforked_num_kids <= 0) len = u_num2str32(ptr, UNotifier::num_connection - UNotifier::min_connection - 1);
|
|
else
|
|
{
|
|
char* start = ptr;
|
|
|
|
*ptr++ = '(';
|
|
ptr += u_num2str32(ptr, UNotifier::num_connection - UNotifier::min_connection - 1);
|
|
*ptr++ = '/';
|
|
ptr += u_num2str32(ptr, U_TOT_CONNECTION - flag_loop); // NB: check for SIGTERM event...
|
|
*ptr++ = ')';
|
|
|
|
len = ptr - start;
|
|
}
|
|
|
|
U_RETURN(len);
|
|
}
|
|
#endif
|
|
|
|
bool UServer_Base::handlerTimeoutConnection(void* cimg)
|
|
{
|
|
U_TRACE(0, "UServer_Base::handlerTimeoutConnection(%p)", cimg)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(pthis)
|
|
U_INTERNAL_ASSERT_POINTER(ptime)
|
|
U_INTERNAL_ASSERT_DIFFERS(timeoutMS, -1)
|
|
|
|
#ifdef U_LOG_ENABLE
|
|
bool from_handlerTime = false;
|
|
#endif
|
|
|
|
if (cimg == 0) cimg = pClientIndex;
|
|
else
|
|
{
|
|
U_INTERNAL_DUMP("pthis = %p handler_inotify = %p ", pthis, handler_inotify)
|
|
|
|
if (cimg == pthis ||
|
|
cimg == handler_inotify)
|
|
{
|
|
U_RETURN(false);
|
|
}
|
|
|
|
# ifdef U_LOG_ENABLE
|
|
from_handlerTime = true;
|
|
# endif
|
|
}
|
|
|
|
if (((UClientImage_Base*)cimg)->handlerTimeout() == U_NOTIFIER_DELETE) // NB: this call set also UClientImage_Base::pthis...
|
|
{
|
|
U_INTERNAL_ASSERT_EQUALS(cimg, UClientImage_Base::pthis) // NB: U_SRV_LOG_WITH_ADDR macro depend on UClientImage_Base::pthis...
|
|
|
|
# ifdef U_LOG_ENABLE
|
|
if (isLog())
|
|
{
|
|
if (from_handlerTime)
|
|
{
|
|
U_SRV_LOG_WITH_ADDR("handlerTime: client connected didn't send any request in %u secs (timeout), close connection",
|
|
ptime->UTimeVal::tv_sec);
|
|
}
|
|
else
|
|
{
|
|
U_SRV_LOG_WITH_ADDR("handlerTimeoutConnection: client connected didn't send any request in %u secs (timeout), close connection",
|
|
last_event - ((UClientImage_Base*)cimg)->last_event);
|
|
}
|
|
}
|
|
# endif
|
|
|
|
U_RETURN(true); // NB: erase item...
|
|
}
|
|
|
|
U_RETURN(false);
|
|
}
|
|
|
|
// define method VIRTUAL of class UEventTime
|
|
|
|
int UServer_Base::UTimeoutConnection::handlerTime()
|
|
{
|
|
U_TRACE(0, "UServer_Base::UTimeoutConnection::handlerTime()")
|
|
|
|
U_INTERNAL_DUMP("UNotifier::num_connection = %d", UNotifier::num_connection)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(ptr_shared_data)
|
|
|
|
if (UNotifier::num_connection > UNotifier::min_connection)
|
|
{
|
|
U_gettimeofday; // NB: optimization if it is enough a time resolution of one second...
|
|
|
|
// there are idle connection... (timeout)
|
|
|
|
# if defined(U_LOG_ENABLE) && defined(DEBUG)
|
|
if (isLog())
|
|
{
|
|
long delta = (u_now->tv_sec - last_event) - ptime->UTimeVal::tv_sec;
|
|
|
|
if (delta >= 1 ||
|
|
delta <= -1)
|
|
{
|
|
U_SRV_LOG("handlerTime: server delta timeout exceed 1 sec: diff %ld sec", delta);
|
|
}
|
|
}
|
|
# endif
|
|
|
|
last_event = u_now->tv_sec;
|
|
|
|
UNotifier::callForAllEntryDynamic(handlerTimeoutConnection);
|
|
}
|
|
|
|
#ifdef U_LOG_ENABLE
|
|
if (U_CNT_PARALLELIZATION)
|
|
#endif
|
|
removeZombies();
|
|
|
|
// ---------------
|
|
// return value:
|
|
// ---------------
|
|
// -1 - normal
|
|
// 0 - monitoring
|
|
// ---------------
|
|
|
|
U_RETURN(0);
|
|
}
|
|
|
|
void UServer_Base::runLoop(const char* user)
|
|
{
|
|
U_TRACE(0, "UServer_Base::runLoop(%S)", user)
|
|
|
|
socket->reusePort();
|
|
|
|
#if !defined(U_SERVER_CAPTIVE_PORTAL) && !defined(_MSWINDOWS_)
|
|
if (bipc == false)
|
|
{
|
|
/**
|
|
* socket->setBufferRCV(128 * 1024);
|
|
* socket->setBufferSND(128 * 1024);
|
|
*
|
|
* Let's say an application just issued a request to send a small block of data. Now, we could
|
|
* either send the data immediately or wait for more data. Some interactive and client-server
|
|
* applications will benefit greatly if we send the data right away. For example, when we are
|
|
* sending a short request and awaiting a large response, the relative overhead is low compared
|
|
* to the total amount of data transferred, and the response time could be much better if the
|
|
* request is sent immediately. This is achieved by setting the TCP_NODELAY option on the socket,
|
|
* which disables the Nagle algorithm.
|
|
*
|
|
* Another way to prevent delays caused by sending useless packets is to use the TCP_QUICKACK option.
|
|
* This option is different from TCP_DEFER_ACCEPT, as it can be used not only to manage the process of
|
|
* connection establishment, but it can be used also during the normal data transfer process. In addition,
|
|
* it can be set on either side of the client-server connection. Delaying sending of the ACK packet could
|
|
* be useful if it is known that the user data will be sent soon, and it is better to set the ACK flag on
|
|
* that data packet to minimize overhead. When the sender is sure that data will be immediately be sent
|
|
* (multiple packets), the TCP_QUICKACK option can be set to 0. The default value of this option is 1 for
|
|
* sockets in the connected state, which will be reset by the kernel to 1 immediately after the first use.
|
|
* (This is a one-time option)
|
|
*
|
|
* Linux (along with some other OSs) includes a TCP_DEFER_ACCEPT option in its TCP implementation.
|
|
* Set on a server-side listening socket, it instructs the kernel not to wait for the final ACK packet
|
|
* and not to initiate the process until the first packet of real data has arrived. After sending the SYN/ACK,
|
|
* the server will then wait for a data packet from a client. Now, only three packets will be sent over the
|
|
* network, and the connection establishment delay will be significantly reduced, which is typical for HTTP.
|
|
* NB: Takes an integer value (seconds)
|
|
*/
|
|
|
|
socket->setTcpNoDelay();
|
|
socket->setTcpFastOpen();
|
|
socket->setTcpQuickAck();
|
|
socket->setTcpDeferAccept();
|
|
|
|
if (set_tcp_keep_alive) socket->setTcpKeepAlive();
|
|
}
|
|
#endif
|
|
|
|
#ifndef _MSWINDOWS_
|
|
if (user)
|
|
{
|
|
if (u_runAsUser(user, false) == false) U_ERROR("set user %S context failed", user);
|
|
|
|
U_SRV_LOG("Server run with user %S permission", user);
|
|
}
|
|
else if (USocket::iBackLog != 1)
|
|
{
|
|
// We don't need these anymore.
|
|
// Good security policy says we get rid of them
|
|
|
|
u_never_need_root();
|
|
u_never_need_group();
|
|
}
|
|
#endif
|
|
|
|
pthis->UEventFd::fd = socket->iSockDesc;
|
|
|
|
UNotifier::init(false);
|
|
|
|
U_INTERNAL_DUMP("UNotifier::min_connection = %d", UNotifier::min_connection)
|
|
|
|
if (UNotifier::min_connection)
|
|
{
|
|
if (binsert) UNotifier::insert(pthis); // NB: we ask to be notified for request of connection (=> accept)
|
|
if (handler_inotify) UNotifier::insert(handler_inotify); // NB: we ask to be notified for change of file system (=> inotify)
|
|
}
|
|
|
|
#ifdef U_LOG_ENABLE
|
|
if (isLog()) ULog::log("Waiting for connection on port %u", port);
|
|
#endif
|
|
|
|
#if defined(ENABLE_THREAD) && defined(HAVE_EPOLL_WAIT) && !defined(USE_LIBEVENT) && defined(U_SERVER_THREAD_APPROACH_SUPPORT)
|
|
if (preforked_num_kids == -1)
|
|
{
|
|
((UThread*)(UNotifier::pthread = U_NEW(UClientThread)))->start(50);
|
|
|
|
proc->_pid = ((UThread*)UNotifier::pthread)->getTID();
|
|
|
|
U_ASSERT(proc->parent())
|
|
}
|
|
#endif
|
|
|
|
#if defined(ENABLE_THREAD) && !defined(_MSWINDOWS_)
|
|
(void) U_SYSCALL(pthread_sigmask, "%d,%p,%p", SIG_UNBLOCK, &mask, 0);
|
|
#endif
|
|
|
|
while (flag_loop)
|
|
{
|
|
if (UNLIKELY(UInterrupt::event_signal_pending))
|
|
{
|
|
UInterrupt::callHandlerSignal();
|
|
|
|
continue;
|
|
}
|
|
|
|
U_INTERNAL_DUMP("ptime = %p handler_inotify = %p UNotifier::num_connection = %u UNotifier::min_connection = %u",
|
|
ptime, handler_inotify, UNotifier::num_connection, UNotifier::min_connection)
|
|
|
|
# if defined(ENABLE_THREAD) && defined(HAVE_EPOLL_WAIT) && !defined(USE_LIBEVENT) && defined(U_SERVER_THREAD_APPROACH_SUPPORT)
|
|
if (preforked_num_kids != -1)
|
|
# endif
|
|
{
|
|
if (UNotifier::min_connection ||
|
|
UNotifier::min_connection < UNotifier::num_connection) // NB: if we have some client we can't go directly on accept() and block on it...
|
|
{
|
|
UNotifier::waitForEvent(ptime);
|
|
|
|
if (UNotifier::empty() == false) continue;
|
|
|
|
return; // NB: no more event manager registered, the child go to exit...
|
|
}
|
|
}
|
|
|
|
// NB: we go directly on accept() and block on it...
|
|
|
|
# ifdef HAVE_EPOLL_WAIT
|
|
U_INTERNAL_ASSERT_EQUALS(UNotifier::add_mask & EPOLLET, 0)
|
|
# endif
|
|
U_INTERNAL_ASSERT_EQUALS(USocket::server_flags & O_NONBLOCK, 0)
|
|
|
|
# if !defined(ENABLE_THREAD) || !defined(HAVE_EPOLL_WAIT) || defined(USE_LIBEVENT) || !defined(U_SERVER_THREAD_APPROACH_SUPPORT)
|
|
U_INTERNAL_ASSERT(UNotifier::min_connection == UNotifier::num_connection)
|
|
# endif
|
|
|
|
(void) pthis->UServer_Base::handlerRead();
|
|
}
|
|
}
|
|
|
|
void UServer_Base::run()
|
|
{
|
|
U_TRACE(1, "UServer_Base::run()")
|
|
|
|
U_INTERNAL_ASSERT_POINTER(pthis)
|
|
|
|
init();
|
|
|
|
if (pluginsHandlerRun() != U_PLUGIN_HANDLER_FINISHED) U_ERROR("Plugins stage run failed");
|
|
|
|
if (u_start_time == 0 &&
|
|
u_setStartTime() == false)
|
|
{
|
|
U_ERROR("System date not updated");
|
|
}
|
|
|
|
if (cfg) cfg->clear();
|
|
|
|
UInterrupt::syscall_restart = false;
|
|
UInterrupt::exit_loop_wait_event_for_signal = true;
|
|
|
|
#if !defined(USE_LIBEVENT) && !defined(USE_RUBY)
|
|
UInterrupt::insert( SIGHUP, (sighandler_t)UServer_Base::handlerForSigHUP); // async signal
|
|
UInterrupt::insert(SIGTERM, (sighandler_t)UServer_Base::handlerForSigTERM); // async signal
|
|
#else
|
|
UInterrupt::setHandlerForSignal( SIGHUP, (sighandler_t)UServer_Base::handlerForSigHUP); // sync signal
|
|
UInterrupt::setHandlerForSignal(SIGTERM, (sighandler_t)UServer_Base::handlerForSigTERM); // sync signal
|
|
#endif
|
|
|
|
int status;
|
|
const char* user = (as_user->empty() ? 0 : as_user->data());
|
|
|
|
/**
|
|
* PREFORK_CHILD number of child server processes created at startup:
|
|
*
|
|
* -1 - thread approach (experimental)
|
|
* 0 - serialize, no forking
|
|
* 1 - classic, forking after accept client
|
|
* >1 - pool of process serialize plus monitoring process
|
|
*/
|
|
|
|
if (monitoring_process)
|
|
{
|
|
/**
|
|
* Main loop for the parent process with the new preforked implementation.
|
|
* The parent is just responsible for keeping a pool of children and they accept connections themselves...
|
|
*/
|
|
|
|
int nkids;
|
|
pid_t pid_to_wait;
|
|
UTimeVal to_sleep(0L, 500L * 1000L);
|
|
|
|
# ifndef U_SERVER_CAPTIVE_PORTAL
|
|
bool baffinity = (preforked_num_kids <= u_get_num_cpu() && u_num_cpu > 1);
|
|
|
|
# ifdef HAVE_SCHED_GETAFFINITY
|
|
if (baffinity) U_SRV_LOG("cpu affinity is to be set; thread count (%d) <= cpu count (%d)", preforked_num_kids, u_num_cpu);
|
|
|
|
U_INTERNAL_DUMP("baffinity = %b", baffinity)
|
|
# endif
|
|
# endif
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(rkids, 0)
|
|
|
|
if (preforked_num_kids <= 0) nkids = 1;
|
|
else
|
|
{
|
|
pid_to_wait = -1;
|
|
nkids = preforked_num_kids;
|
|
}
|
|
|
|
U_INTERNAL_DUMP("nkids = %d", nkids)
|
|
|
|
while (flag_loop)
|
|
{
|
|
u_need_root(false);
|
|
|
|
while (rkids < nkids)
|
|
{
|
|
if (proc->fork() &&
|
|
proc->parent())
|
|
{
|
|
++rkids;
|
|
|
|
U_INTERNAL_DUMP("up to %u children, UNotifier::num_connection = %d", rkids, UNotifier::num_connection)
|
|
|
|
pid = proc->pid();
|
|
|
|
cpu_set_t cpuset;
|
|
|
|
# ifndef U_SERVER_CAPTIVE_PORTAL
|
|
if (baffinity) u_bind2cpu(pid, rkids); // Pin the process to a particular core...
|
|
# endif
|
|
|
|
CPU_ZERO(&cpuset);
|
|
|
|
# if defined(HAVE_SCHED_GETAFFINITY) && !defined(U_SERVER_CAPTIVE_PORTAL)
|
|
(void) U_SYSCALL(sched_getaffinity, "%d,%d,%p", pid, sizeof(cpuset), &cpuset);
|
|
|
|
U_INTERNAL_DUMP("cpuset = %ld %B", CPUSET_BITS(&cpuset)[0], CPUSET_BITS(&cpuset)[0])
|
|
# endif
|
|
|
|
U_SRV_LOG("Started new child (pid %d), up to %u children, affinity mask: %x", pid, rkids, CPUSET_BITS(&cpuset)[0]);
|
|
|
|
# ifndef U_SERVER_CAPTIVE_PORTAL
|
|
if (set_realtime_priority &&
|
|
u_switch_to_realtime_priority(pid) == false)
|
|
{
|
|
U_WARNING("Cannot set posix realtime scheduling policy");
|
|
}
|
|
# endif
|
|
|
|
if (preforked_num_kids <= 0) pid_to_wait = pid;
|
|
|
|
# ifdef ENABLE_THREAD
|
|
if (u_pthread_time)
|
|
{
|
|
# ifdef DEBUG
|
|
(void) U_SYSCALL(gettimeofday, "%p,%p", u_now, 0);
|
|
|
|
((UTimeThread*)u_pthread_time)->before.set(*u_now);
|
|
# endif
|
|
((UTimeThread*)u_pthread_time)->watch_counter = 0;
|
|
}
|
|
# endif
|
|
}
|
|
|
|
if (proc->child())
|
|
{
|
|
U_INTERNAL_DUMP("child = %P UNotifier::num_connection = %d", UNotifier::num_connection)
|
|
|
|
/**
|
|
* POSIX.1-1990 disallowed setting the action for SIGCHLD to SIG_IGN.
|
|
* POSIX.1-2001 allows this possibility, so that ignoring SIGCHLD can be
|
|
* used to prevent the creation of zombies (see wait(2)). Nevertheless, the
|
|
* historical BSD and System V behaviors for ignoring SIGCHLD differ, so that
|
|
* the only completely portable method of ensuring that terminated children do
|
|
* not become zombies is to catch the SIGCHLD signal and perform a wait(2) or similar.
|
|
*
|
|
* NB: we cannot use SIGCHLD to avoid zombie for parallelization because in this way we
|
|
* interfere with waiting of cgi-bin processing that write on pipe and also to know
|
|
* exit status from script...
|
|
*
|
|
* UInterrupt::setHandlerForSignal(SIGCHLD, (sighandler_t)UServer_Base::handlerForSigCHLD);
|
|
*/
|
|
|
|
// NB: we can't use UInterrupt::erase() because it restore the old action (UInterrupt::init)...
|
|
UInterrupt::setHandlerForSignal(SIGHUP, (sighandler_t)SIG_IGN);
|
|
|
|
if (pluginsHandlerFork() != U_PLUGIN_HANDLER_FINISHED) U_ERROR("Plugins stage fork failed");
|
|
|
|
runLoop(user);
|
|
|
|
return;
|
|
}
|
|
|
|
// Don't start them too quickly, or we might overwhelm a machine that's having trouble
|
|
|
|
to_sleep.nanosleep();
|
|
|
|
# ifdef U_SERVER_CAPTIVE_PORTAL
|
|
if (proc->_pid == -1)
|
|
{
|
|
monitoring_process = false;
|
|
|
|
goto no_monitoring_process;
|
|
}
|
|
# endif
|
|
}
|
|
|
|
// wait for any children to exit, and then start some more
|
|
|
|
# if defined(ENABLE_THREAD) && !defined(_MSWINDOWS_)
|
|
(void) U_SYSCALL(pthread_sigmask, "%d,%p,%p", SIG_UNBLOCK, &mask, 0);
|
|
# endif
|
|
|
|
u_dont_need_root();
|
|
|
|
pid = UProcess::waitpid(pid_to_wait, &status, 0);
|
|
|
|
U_INTERNAL_DUMP("rkids = %d", rkids)
|
|
|
|
if (rkids == 0) // NB: check for SIGHUP event...
|
|
{
|
|
manageSigHUP();
|
|
|
|
continue;
|
|
}
|
|
|
|
if (pid > 0 &&
|
|
flag_loop) // NB: check for SIGTERM event...
|
|
{
|
|
U_INTERNAL_ASSERT_MAJOR(rkids, 0)
|
|
|
|
--rkids;
|
|
|
|
# ifndef U_SERVER_CAPTIVE_PORTAL
|
|
baffinity = false;
|
|
# endif
|
|
|
|
U_INTERNAL_DUMP("down to %u children", rkids)
|
|
|
|
// Another little safety brake here: since children should not
|
|
// exit too quickly, pausing before starting them should be harmless
|
|
|
|
if (USemaphore::checkForDeadLock(to_sleep) == false) to_sleep.nanosleep();
|
|
|
|
# ifdef U_LOG_ENABLE
|
|
if (isLog())
|
|
{
|
|
char buffer[128];
|
|
|
|
ULog::log("%sWARNING: child (pid %d) exited with value %d (%s), down to %u children",
|
|
mod_name[0], pid, status, UProcess::exitInfo(buffer, status), rkids);
|
|
}
|
|
# endif
|
|
}
|
|
}
|
|
|
|
U_INTERNAL_ASSERT(proc->parent())
|
|
}
|
|
else
|
|
{
|
|
# ifdef U_SERVER_CAPTIVE_PORTAL
|
|
no_monitoring_process:
|
|
if (pluginsHandlerFork() != U_PLUGIN_HANDLER_FINISHED) U_ERROR("Plugins stage fork failed");
|
|
# endif
|
|
|
|
runLoop(user);
|
|
}
|
|
|
|
if (pluginsHandlerStop() != U_PLUGIN_HANDLER_FINISHED) U_WARNING("Plugins stage stop failed");
|
|
|
|
status = proc->waitAll(2);
|
|
|
|
if (status >= U_FAILED_SOME)
|
|
{
|
|
UProcess::kill(0, SIGKILL); // SIGKILL is sent to every process in the process group of the calling process...
|
|
|
|
(void) proc->waitAll(2);
|
|
}
|
|
|
|
#ifdef U_LOG_ENABLE
|
|
if (isLog() &&
|
|
flag_sigterm)
|
|
{
|
|
logMemUsage("SIGTERM");
|
|
}
|
|
#endif
|
|
|
|
if (proc->parent() ||
|
|
preforked_num_kids <= 0)
|
|
{
|
|
closeLog();
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
pthis->deallocate();
|
|
#endif
|
|
}
|
|
|
|
void UServer_Base::removeZombies()
|
|
{
|
|
U_TRACE(0, "UServer_Base::removeZombies()")
|
|
|
|
U_INTERNAL_ASSERT_POINTER(ptr_shared_data)
|
|
|
|
#ifndef U_LOG_ENABLE
|
|
(void) UProcess::removeZombies();
|
|
#else
|
|
uint32_t n = UProcess::removeZombies();
|
|
|
|
if (n) U_SRV_LOG("removed %u zombies - current parallelization (%d)", n, U_CNT_PARALLELIZATION);
|
|
#endif
|
|
}
|
|
|
|
// it creates a copy of itself, return true if parent...
|
|
|
|
pid_t UServer_Base::startNewChild()
|
|
{
|
|
U_TRACE(0, "UServer_Base::startNewChild()")
|
|
|
|
UProcess p;
|
|
|
|
if (p.fork() &&
|
|
p.parent())
|
|
{
|
|
pid_t pid = p.pid();
|
|
|
|
# ifndef U_LOG_ENABLE
|
|
(void) UProcess::removeZombies();
|
|
# else
|
|
uint32_t n = UProcess::removeZombies();
|
|
|
|
U_CNT_PARALLELIZATION++;
|
|
|
|
U_SRV_LOG("Started new child (pid %d) for parallelization (%d) - removed %u zombies", pid, U_CNT_PARALLELIZATION, n);
|
|
# endif
|
|
|
|
U_RETURN(pid); // parent
|
|
}
|
|
|
|
if (p.child()) U_RETURN(0);
|
|
|
|
U_RETURN(-1);
|
|
}
|
|
|
|
__noreturn void UServer_Base::endNewChild()
|
|
{
|
|
U_TRACE(0, "UServer_Base::endNewChild()")
|
|
|
|
#ifdef DEBUG
|
|
UInterrupt::setHandlerForSignal(SIGHUP, (sighandler_t)SIG_IGN);
|
|
UInterrupt::setHandlerForSignal(SIGTERM, (sighandler_t)SIG_IGN);
|
|
#endif
|
|
|
|
#ifdef U_LOG_ENABLE
|
|
if (LIKELY(U_CNT_PARALLELIZATION)) U_CNT_PARALLELIZATION--;
|
|
|
|
U_INTERNAL_DUMP("cnt_parallelization = %d", U_CNT_PARALLELIZATION)
|
|
|
|
U_SRV_LOG("child for parallelization ended (%d)", U_CNT_PARALLELIZATION);
|
|
#endif
|
|
|
|
U_EXIT(0);
|
|
}
|
|
|
|
bool UServer_Base::startParallelization(uint32_t nclient)
|
|
{
|
|
U_TRACE(0, "UServer_Base::startParallelization(%u)", nclient)
|
|
|
|
if (isParallelizationGoingToStart(nclient))
|
|
{
|
|
pid_t pid = startNewChild();
|
|
|
|
if (pid > 0)
|
|
{
|
|
// NB: from now it is responsability of the child to services the request from the client on the same connection...
|
|
|
|
U_ClientImage_close = true;
|
|
U_ClientImage_parallelization = 2; // 2 => parent of parallelization
|
|
|
|
UClientImage_Base::resetPipeline();
|
|
UClientImage_Base::setRequestProcessed();
|
|
|
|
U_ASSERT(isParallelizationParent())
|
|
|
|
U_RETURN(true);
|
|
}
|
|
|
|
if (pid == 0)
|
|
{
|
|
U_ClientImage_parallelization = 1; // 1 => child of parallelization
|
|
|
|
U_ASSERT(isParallelizationChild())
|
|
}
|
|
}
|
|
|
|
U_INTERNAL_DUMP("U_ClientImage_close = %b", U_ClientImage_close)
|
|
|
|
U_RETURN(false);
|
|
}
|
|
|
|
void UServer_Base::logCommandMsgError(const char* cmd, bool balways)
|
|
{
|
|
U_TRACE(0, "UServer_Base::logCommandMsgError(%S,%b)", cmd, balways)
|
|
|
|
#ifdef U_LOG_ENABLE
|
|
if (isLog())
|
|
{
|
|
if (UCommand::setMsgError(cmd, !balways) || balways) ULog::log("%s%.*s", mod_name[0], u_buffer_len, u_buffer);
|
|
|
|
errno = 0;
|
|
u_buffer_len = 0;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// DEBUG
|
|
|
|
#if defined(U_STDCPP_ENABLE) && defined(DEBUG)
|
|
const char* UServer_Base::dump(bool reset) const
|
|
{
|
|
*UObjectIO::os << "port " << port << '\n'
|
|
<< "map_size " << map_size << '\n'
|
|
<< "flag_loop " << flag_loop << '\n'
|
|
<< "timeoutMS " << timeoutMS << '\n'
|
|
<< "last_event " << last_event << '\n'
|
|
<< "verify_mode " << verify_mode << '\n'
|
|
<< "shared_data_add " << shared_data_add << '\n'
|
|
<< "ptr_static_date " << (void*)ptr_static_date << '\n'
|
|
<< "ptr_shared_data " << (void*)ptr_shared_data << '\n'
|
|
<< "preforked_num_kids " << preforked_num_kids << '\n'
|
|
<< "log (ULog " << (void*)log << ")\n"
|
|
<< "socket (USocket " << (void*)socket << ")\n"
|
|
<< "host (UString " << (void*)host << ")\n"
|
|
<< "dh_file (UString " << (void*)dh_file << ")\n"
|
|
<< "ca_file (UString " << (void*)ca_file << ")\n"
|
|
<< "ca_path (UString " << (void*)ca_path << ")\n"
|
|
# ifdef U_ACL_SUPPORT
|
|
<< "allow_IP (UString " << (void*)allow_IP << ")\n"
|
|
# endif
|
|
<< "key_file (UString " << (void*)key_file << ")\n"
|
|
<< "password (UString " << (void*)password << ")\n"
|
|
<< "cert_file (UString " << (void*)cert_file << ")\n"
|
|
<< "name_sock (UString " << (void*)name_sock << ")\n"
|
|
<< "IP_address (UString " << (void*)IP_address << ")\n"
|
|
# ifdef U_WELCOME_SUPPORT
|
|
<< "msg_welcome (UString " << (void*)msg_welcome << ")\n"
|
|
# endif
|
|
<< "document_root (UString " << (void*)document_root << ")\n"
|
|
<< "proc (UProcess " << (void*)proc << ')';
|
|
|
|
if (reset)
|
|
{
|
|
UObjectIO::output();
|
|
|
|
return UObjectIO::buffer_output;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|