mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2024-12-12 10:04:14 +02:00
Add SIGTERM and SIGHUP handling to TLS server.
SIGHUP allows the configuration to be reloaded. Note that the configuration will not be updated in child processes that have already started. SIGTERM terminates the server process gracefully and sends SIGTERM to all child processes. This also gives the tests an easy way to stop the server.
This commit is contained in:
parent
49145d72ba
commit
7b3ea883c7
@ -96,10 +96,14 @@
|
||||
<release-development-list>
|
||||
<release-item>
|
||||
<commit subject="Rename server-start command to server."/>
|
||||
<commit subject="Add SIGTERM and SIGHUP handling to TLS server.">
|
||||
<github-pull-request id="1572"/>
|
||||
</commit>
|
||||
|
||||
<release-item-contributor-list>
|
||||
<release-item-contributor id="david.steele"/>
|
||||
<release-item-reviewer id="stephen.frost"/>
|
||||
<release-item-reviewer id="reid.thompson"/>
|
||||
<!-- Actually tester, but we don't have a tag for that yet -->
|
||||
<release-item-reviewer id="andrew.lecuyer"/>
|
||||
</release-item-contributor-list>
|
||||
|
@ -8,74 +8,206 @@ Server Command
|
||||
#include "command/remote/remote.h"
|
||||
#include "command/server/server.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/exit.h"
|
||||
#include "common/fork.h"
|
||||
#include "common/io/socket/server.h"
|
||||
#include "common/io/tls/server.h"
|
||||
#include "config/config.h"
|
||||
#include "config/load.h"
|
||||
#include "protocol/helper.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Local variables
|
||||
***********************************************************************************************************************************/
|
||||
static struct ServerLocal
|
||||
{
|
||||
MemContext *memContext; // Mem context for server
|
||||
|
||||
unsigned int argListSize; // Argument list size
|
||||
const char **argList; // Argument list
|
||||
|
||||
List *processList; // List of child processes
|
||||
|
||||
bool sigHup; // SIGHUP was caught
|
||||
bool sigTerm; // SIGTERM was caught
|
||||
|
||||
IoServer *socketServer; // Socket server
|
||||
IoServer *tlsServer; // TLS server
|
||||
} serverLocal;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Initialization can be redone when options change
|
||||
***********************************************************************************************************************************/
|
||||
static void
|
||||
cmdServerInit(void)
|
||||
{
|
||||
// Initialize mem context
|
||||
if (serverLocal.memContext == NULL)
|
||||
{
|
||||
MEM_CONTEXT_BEGIN(memContextTop())
|
||||
{
|
||||
MEM_CONTEXT_NEW_BEGIN("Server")
|
||||
{
|
||||
serverLocal.memContext = MEM_CONTEXT_NEW();
|
||||
serverLocal.processList = lstNewP(sizeof(pid_t));
|
||||
}
|
||||
MEM_CONTEXT_NEW_END();
|
||||
}
|
||||
MEM_CONTEXT_END();
|
||||
}
|
||||
|
||||
MEM_CONTEXT_BEGIN(serverLocal.memContext)
|
||||
{
|
||||
// Free old servers
|
||||
ioServerFree(serverLocal.socketServer);
|
||||
ioServerFree(serverLocal.tlsServer);
|
||||
|
||||
// Create new servers
|
||||
serverLocal.socketServer = sckServerNew(
|
||||
cfgOptionStr(cfgOptTlsServerAddress), cfgOptionUInt(cfgOptTlsServerPort), cfgOptionUInt64(cfgOptProtocolTimeout));
|
||||
serverLocal.tlsServer = tlsServerNew(
|
||||
cfgOptionStr(cfgOptTlsServerAddress), cfgOptionStr(cfgOptTlsServerCaFile), cfgOptionStr(cfgOptTlsServerKeyFile),
|
||||
cfgOptionStr(cfgOptTlsServerCertFile), cfgOptionUInt64(cfgOptProtocolTimeout));
|
||||
}
|
||||
MEM_CONTEXT_END();
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Handlers to set flags on signals
|
||||
***********************************************************************************************************************************/
|
||||
static void
|
||||
cmdServerSigHup(const int signalType)
|
||||
{
|
||||
(void)signalType;
|
||||
serverLocal.sigHup = true;
|
||||
}
|
||||
|
||||
static void
|
||||
cmdServerSigTerm(const int signalType)
|
||||
{
|
||||
(void)signalType;
|
||||
serverLocal.sigTerm = true;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Handler to reap child processes
|
||||
***********************************************************************************************************************************/
|
||||
static void
|
||||
cmdServerSigChild(const int signalType, siginfo_t *signalInfo, void *context)
|
||||
{
|
||||
(void)signalType;
|
||||
(void)context;
|
||||
|
||||
ASSERT(signalInfo->si_code == CLD_EXITED);
|
||||
|
||||
// Find the process and remove it
|
||||
for (unsigned int processIdx = 0; processIdx < lstSize(serverLocal.processList); processIdx++)
|
||||
{
|
||||
if (*(int *)lstGet(serverLocal.processList, processIdx) == signalInfo->si_pid)
|
||||
lstRemoveIdx(serverLocal.processList, processIdx);
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
void
|
||||
cmdServer(uint64_t connectionMax)
|
||||
cmdServer(const unsigned int argListSize, const char *argList[])
|
||||
{
|
||||
FUNCTION_LOG_VOID(logLevelDebug);
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(UINT, argListSize);
|
||||
FUNCTION_LOG_PARAM(CHARPY, argList);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(connectionMax > 0);
|
||||
// Initialize server
|
||||
cmdServerInit();
|
||||
|
||||
// Set arguments used for reload
|
||||
serverLocal.argListSize = argListSize;
|
||||
serverLocal.argList = argList;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
IoServer *const tlsServer = tlsServerNew(
|
||||
cfgOptionStr(cfgOptTlsServerAddress), cfgOptionStr(cfgOptTlsServerCaFile), cfgOptionStr(cfgOptTlsServerKeyFile),
|
||||
cfgOptionStr(cfgOptTlsServerCertFile), cfgOptionUInt64(cfgOptProtocolTimeout));
|
||||
IoServer *const socketServer = sckServerNew(
|
||||
cfgOptionStr(cfgOptTlsServerAddress), cfgOptionUInt(cfgOptTlsServerPort), cfgOptionUInt64(cfgOptProtocolTimeout));
|
||||
// Set signal handlers
|
||||
sigaction(SIGHUP, &(struct sigaction){.sa_handler = cmdServerSigHup}, NULL);
|
||||
sigaction(SIGTERM, &(struct sigaction){.sa_handler = cmdServerSigTerm}, NULL);
|
||||
sigaction(
|
||||
SIGCHLD, &(struct sigaction){.sa_sigaction = cmdServerSigChild, .sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_SIGINFO},
|
||||
NULL);
|
||||
|
||||
// Accept connections until connection max is reached
|
||||
// Accept connections indefinitely. The only way to exit this loop is for the process to receive a signal.
|
||||
do
|
||||
{
|
||||
// Accept a new connection
|
||||
IoSession *const socketSession = ioServerAccept(socketServer, NULL);
|
||||
IoSession *const socketSession = ioServerAccept(serverLocal.socketServer, NULL);
|
||||
|
||||
// Fork off the child process
|
||||
pid_t pid = forkSafe();
|
||||
|
||||
if (pid == 0)
|
||||
if (socketSession != NULL)
|
||||
{
|
||||
// Close the server socket so we don't hold the port open if the parent exits first
|
||||
ioServerFree(socketServer);
|
||||
// Fork off the child process
|
||||
pid_t pid = forkSafe();
|
||||
|
||||
// Disable logging and close log file
|
||||
logClose();
|
||||
if (pid == 0)
|
||||
{
|
||||
// Reset SIGCHLD to default
|
||||
sigaction(SIGCHLD, &(struct sigaction){.sa_handler = SIG_DFL}, NULL);
|
||||
|
||||
// Detach from parent process
|
||||
forkDetach();
|
||||
// Set standard signal handlers
|
||||
exitInit();
|
||||
|
||||
// Start standard remote processing if a server is returned
|
||||
ProtocolServer *server = protocolServer(tlsServer, socketSession);
|
||||
// Close the server socket so we don't hold the port open if the parent exits first
|
||||
ioServerFree(serverLocal.socketServer);
|
||||
|
||||
if (server != NULL)
|
||||
cmdRemote(server);
|
||||
// Disable logging and close log file
|
||||
logClose();
|
||||
|
||||
break;
|
||||
}
|
||||
// Wait for first fork to exit
|
||||
else
|
||||
{
|
||||
// The process that was just forked should return immediately
|
||||
int processStatus;
|
||||
// Start standard remote processing if a server is returned
|
||||
ProtocolServer *server = protocolServer(serverLocal.tlsServer, socketSession);
|
||||
|
||||
THROW_ON_SYS_ERROR(waitpid(pid, &processStatus, 0) == -1, ExecuteError, "unable to wait for forked process");
|
||||
if (server != NULL)
|
||||
cmdRemote(server);
|
||||
|
||||
// The first fork should exit with success. If not, something went wrong during the second fork.
|
||||
CHECK(ExecuteError, WIFEXITED(processStatus) && WEXITSTATUS(processStatus) == 0, "error on first fork");
|
||||
break;
|
||||
}
|
||||
// Add process to list
|
||||
else
|
||||
lstAdd(serverLocal.processList, &pid);
|
||||
|
||||
// Free the socket since the child is now using it
|
||||
ioSessionFree(socketSession);
|
||||
}
|
||||
|
||||
// Free the socket since the child is now using it
|
||||
ioSessionFree(socketSession);
|
||||
// Reload configuration
|
||||
if (serverLocal.sigHup)
|
||||
{
|
||||
LOG_DETAIL("configuration reload begin");
|
||||
|
||||
// Reload configuration
|
||||
cfgLoad(serverLocal.argListSize, serverLocal.argList);
|
||||
|
||||
// Reinitialize server
|
||||
cmdServerInit();
|
||||
|
||||
LOG_DETAIL("configuration reload end");
|
||||
|
||||
// Reset flag
|
||||
serverLocal.sigHup = false;
|
||||
}
|
||||
}
|
||||
while (--connectionMax > 0);
|
||||
while (!serverLocal.sigTerm);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
// Terminate any remaining children on SIGTERM. Disable the callback so it does not fire in the middle of the loop.
|
||||
if (serverLocal.sigTerm)
|
||||
{
|
||||
sigaction(SIGCHLD, &(struct sigaction){.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT}, NULL);
|
||||
|
||||
for (unsigned int processIdx = 0; processIdx < lstSize(serverLocal.processList); processIdx++)
|
||||
{
|
||||
pid_t pid = *(int *)lstGet(serverLocal.processList, processIdx);
|
||||
|
||||
LOG_WARN_FMT("terminate child process %d", pid);
|
||||
kill(pid, SIGTERM);
|
||||
}
|
||||
}
|
||||
|
||||
FUNCTION_LOG_RETURN_VOID();
|
||||
}
|
||||
|
@ -9,6 +9,6 @@ Server Command
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
void cmdServer(uint64_t connectionMax);
|
||||
void cmdServer(unsigned int argListSize, const char *argList[]);
|
||||
|
||||
#endif
|
||||
|
@ -96,18 +96,21 @@ sckServerAccept(THIS_VOID, IoSession *const session)
|
||||
|
||||
int serverSocket = accept(this->socket, (struct sockaddr *)&addr, &len);
|
||||
|
||||
THROW_ON_SYS_ERROR(serverSocket == -1, FileOpenError, "unable to accept socket");
|
||||
|
||||
// Create socket session
|
||||
sckOptionSet(serverSocket);
|
||||
|
||||
MEM_CONTEXT_PRIOR_BEGIN()
|
||||
if (serverSocket != -1)
|
||||
{
|
||||
result = sckSessionNew(ioSessionRoleServer, serverSocket, this->address, this->port, this->timeout);
|
||||
}
|
||||
MEM_CONTEXT_PRIOR_END();
|
||||
// Create socket session
|
||||
sckOptionSet(serverSocket);
|
||||
|
||||
statInc(SOCKET_STAT_SESSION_STR);
|
||||
MEM_CONTEXT_PRIOR_BEGIN()
|
||||
{
|
||||
result = sckSessionNew(ioSessionRoleServer, serverSocket, this->address, this->port, this->timeout);
|
||||
}
|
||||
MEM_CONTEXT_PRIOR_END();
|
||||
|
||||
statInc(SOCKET_STAT_SESSION_STR);
|
||||
}
|
||||
else
|
||||
THROW_ON_SYS_ERROR(errno != EINTR, FileOpenError, "unable to accept socket");
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
|
@ -236,7 +236,7 @@ main(int argListSize, const char *argList[])
|
||||
// Server command
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
case cfgCmdServer:
|
||||
cmdServer(UINT64_MAX);
|
||||
cmdServer((unsigned int)argListSize, argList);
|
||||
break;
|
||||
|
||||
// Server ping command
|
||||
|
@ -1,6 +1,7 @@
|
||||
/***********************************************************************************************************************************
|
||||
Test Server Command
|
||||
***********************************************************************************************************************************/
|
||||
#include "common/exit.h"
|
||||
#include "storage/posix/storage.h"
|
||||
#include "storage/remote/storage.h"
|
||||
|
||||
@ -26,7 +27,7 @@ testRun(void)
|
||||
{
|
||||
TEST_TITLE("server");
|
||||
|
||||
HRN_FORK_BEGIN(.timeout = 5000)
|
||||
HRN_FORK_BEGIN(.timeout = 15000)
|
||||
{
|
||||
HRN_FORK_CHILD_BEGIN(.prefix = "client repo")
|
||||
{
|
||||
@ -109,27 +110,72 @@ testRun(void)
|
||||
}
|
||||
HRN_FORK_CHILD_END();
|
||||
|
||||
HRN_FORK_PARENT_BEGIN(.prefix = "server")
|
||||
HRN_FORK_PARENT_BEGIN(.prefix = "client control")
|
||||
{
|
||||
StringList *argList = strLstNew();
|
||||
hrnCfgArgRawZ(argList, cfgOptTlsServerCaFile, HRN_SERVER_CA);
|
||||
hrnCfgArgRawZ(argList, cfgOptTlsServerCertFile, HRN_SERVER_CERT);
|
||||
hrnCfgArgRawZ(argList, cfgOptTlsServerKeyFile, HRN_SERVER_KEY);
|
||||
hrnCfgArgRawZ(argList, cfgOptTlsServerAuth, "pgbackrest-client=db");
|
||||
hrnCfgArgRawFmt(argList, cfgOptTlsServerPort, "%u", hrnServerPort(0));
|
||||
HRN_CFG_LOAD(cfgCmdServer, argList);
|
||||
HRN_FORK_BEGIN(.timeout = 15000)
|
||||
{
|
||||
HRN_FORK_CHILD_BEGIN(.prefix = "server")
|
||||
{
|
||||
// Write a config file to demonstrate that options are loaded and reloaded
|
||||
HRN_STORAGE_PUT_Z(
|
||||
storageTest,
|
||||
"pgbackrest.conf",
|
||||
"[global]\n"
|
||||
CFGOPT_TLS_SERVER_CA_FILE "=" HRN_SERVER_CA "\n"
|
||||
CFGOPT_TLS_SERVER_CERT_FILE "=" HRN_SERVER_CERT "\n"
|
||||
CFGOPT_TLS_SERVER_KEY_FILE "=" HRN_SERVER_KEY "\n"
|
||||
CFGOPT_TLS_SERVER_AUTH "=pgbackrest-client=db\n"
|
||||
"repo1-path=" TEST_PATH "/repo\n");
|
||||
|
||||
// Write a config file to demonstrate that settings are loaded
|
||||
HRN_STORAGE_PUT_Z(storageTest, "pgbackrest.conf", "[global]\nrepo1-path=" TEST_PATH "/repo");
|
||||
StringList *argList = strLstNew();
|
||||
hrnCfgArgRawZ(argList, cfgOptConfig, TEST_PATH "/pgbackrest.conf");
|
||||
hrnCfgArgRawFmt(argList, cfgOptTlsServerPort, "%u", hrnServerPort(0));
|
||||
HRN_CFG_LOAD(cfgCmdServer, argList);
|
||||
|
||||
// Get pid of this process to identify child process later
|
||||
pid_t pid = getpid();
|
||||
// Init exit signal handlers
|
||||
exitInit();
|
||||
|
||||
TEST_RESULT_VOID(cmdServer(3), "server");
|
||||
// No log testing needed
|
||||
harnessLogLevelSet(logLevelError);
|
||||
|
||||
// If this is a child process then exit immediately
|
||||
if (pid != getpid())
|
||||
exit(0);
|
||||
// Add a fake pid to ensure SIGTERM is sent to unterminated children
|
||||
cmdServerInit();
|
||||
|
||||
int fakePid = INT_MAX;
|
||||
lstAdd(serverLocal.processList, &fakePid);
|
||||
|
||||
// Get pid of this process to identify child process later
|
||||
pid_t pid = getpid();
|
||||
|
||||
// Add parameters to arg list required for a reload
|
||||
strLstInsert(argList, 0, cfgExe());
|
||||
strLstAddZ(argList, CFGCMD_SERVER);
|
||||
|
||||
TEST_RESULT_VOID(cmdServer(strLstSize(argList), strLstPtr(argList)), "server");
|
||||
|
||||
// If this is a child process then exit immediately
|
||||
if (pid != getpid())
|
||||
{
|
||||
HRN_FORK_CHILD_NOTIFY_PUT();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
HRN_FORK_CHILD_END();
|
||||
|
||||
HRN_FORK_PARENT_BEGIN(.prefix = "server control")
|
||||
{
|
||||
// Wait for forked server processes to exit
|
||||
HRN_FORK_PARENT_NOTIFY_GET(0);
|
||||
kill(HRN_FORK_PROCESS_ID(0), SIGHUP);
|
||||
HRN_FORK_PARENT_NOTIFY_GET(0);
|
||||
HRN_FORK_PARENT_NOTIFY_GET(0);
|
||||
|
||||
// Send term to server processes
|
||||
kill(HRN_FORK_PROCESS_ID(0), SIGTERM);
|
||||
}
|
||||
HRN_FORK_PARENT_END();
|
||||
}
|
||||
HRN_FORK_END();
|
||||
|
||||
// Wait for both child processes to exit
|
||||
HRN_FORK_PARENT_NOTIFY_GET(0);
|
||||
@ -157,7 +203,7 @@ testRun(void)
|
||||
|
||||
TEST_ERROR(cmdServerPing(), ParamInvalidError, "extra parameters found");
|
||||
|
||||
HRN_FORK_BEGIN(.timeout = 5000)
|
||||
HRN_FORK_BEGIN(.timeout = 15000)
|
||||
{
|
||||
|
||||
HRN_FORK_CHILD_BEGIN(.prefix = "client")
|
||||
@ -185,24 +231,52 @@ testRun(void)
|
||||
}
|
||||
HRN_FORK_CHILD_END();
|
||||
|
||||
HRN_FORK_PARENT_BEGIN(.prefix = "server")
|
||||
HRN_FORK_PARENT_BEGIN(.prefix = "client control")
|
||||
{
|
||||
StringList *argList = strLstNew();
|
||||
hrnCfgArgRawZ(argList, cfgOptTlsServerCaFile, HRN_SERVER_CA);
|
||||
hrnCfgArgRawZ(argList, cfgOptTlsServerCertFile, HRN_SERVER_CERT);
|
||||
hrnCfgArgRawZ(argList, cfgOptTlsServerKeyFile, HRN_SERVER_KEY);
|
||||
hrnCfgArgRawZ(argList, cfgOptTlsServerAuth, "bogus=*");
|
||||
hrnCfgArgRawFmt(argList, cfgOptTlsServerPort, "%u", hrnServerPort(0));
|
||||
HRN_CFG_LOAD(cfgCmdServer, argList);
|
||||
HRN_FORK_BEGIN(.timeout = 15000)
|
||||
{
|
||||
HRN_FORK_CHILD_BEGIN(.prefix = "server")
|
||||
{
|
||||
StringList *argList = strLstNew();
|
||||
hrnCfgArgRawZ(argList, cfgOptTlsServerCaFile, HRN_SERVER_CA);
|
||||
hrnCfgArgRawZ(argList, cfgOptTlsServerCertFile, HRN_SERVER_CERT);
|
||||
hrnCfgArgRawZ(argList, cfgOptTlsServerKeyFile, HRN_SERVER_KEY);
|
||||
hrnCfgArgRawZ(argList, cfgOptTlsServerAuth, "bogus=*");
|
||||
hrnCfgArgRawFmt(argList, cfgOptTlsServerPort, "%u", hrnServerPort(0));
|
||||
HRN_CFG_LOAD(cfgCmdServer, argList);
|
||||
|
||||
// Get pid of this process to identify child process later
|
||||
pid_t pid = getpid();
|
||||
// Init exit signal handlers
|
||||
exitInit();
|
||||
|
||||
TEST_RESULT_VOID(cmdServer(2), "server");
|
||||
// No log testing needed
|
||||
harnessLogLevelSet(logLevelError);
|
||||
|
||||
// If this is a child process then exit immediately
|
||||
if (pid != getpid())
|
||||
exit(0);
|
||||
// Get pid of this process to identify child process later
|
||||
pid_t pid = getpid();
|
||||
|
||||
TEST_RESULT_VOID(cmdServer(strLstSize(argList), strLstPtr(argList)), "server");
|
||||
|
||||
// If this is a child process then exit immediately
|
||||
if (pid != getpid())
|
||||
{
|
||||
HRN_FORK_CHILD_NOTIFY_PUT();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
HRN_FORK_CHILD_END();
|
||||
|
||||
HRN_FORK_PARENT_BEGIN(.prefix = "server control")
|
||||
{
|
||||
// Wait for forked child processes to exit
|
||||
HRN_FORK_PARENT_NOTIFY_GET(0);
|
||||
HRN_FORK_PARENT_NOTIFY_GET(0);
|
||||
|
||||
// Send term to child processes
|
||||
kill(HRN_FORK_PROCESS_ID(0), SIGTERM);
|
||||
}
|
||||
HRN_FORK_PARENT_END();
|
||||
}
|
||||
HRN_FORK_END();
|
||||
|
||||
// Wait for child process to exit
|
||||
HRN_FORK_PARENT_NOTIFY_GET(0);
|
||||
|
@ -127,6 +127,15 @@ static const char *const testClientBadCa =
|
||||
"qGj7FtRiSdjkZ7pmNpma6ycPR0RBZyL3aHnig+DDfRRt8TgrZzY3aXBReONb\n"
|
||||
"-----END CERTIFICATE-----";
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Test signal handler that does nothing
|
||||
***********************************************************************************************************************************/
|
||||
static void
|
||||
testSignalHandler(const int signalType)
|
||||
{
|
||||
(void)signalType;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Test Run
|
||||
***********************************************************************************************************************************/
|
||||
@ -299,7 +308,7 @@ testRun(void)
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("SocketClient"))
|
||||
if (testBegin("SocketClient/SocketServer"))
|
||||
{
|
||||
IoClient *client = NULL;
|
||||
|
||||
@ -311,6 +320,37 @@ testRun(void)
|
||||
// This address should not be in use in a test environment -- if it is the test will fail
|
||||
TEST_ASSIGN(client, sckClientNew(STRDEF("172.31.255.255"), hrnServerPort(0), 100, 100), "new client");
|
||||
TEST_ERROR_FMT(ioClientOpen(client), HostConnectError, "timeout connecting to '172.31.255.255:%u'", hrnServerPort(0));
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("sckServerAccept() returns NULL on interrupt");
|
||||
|
||||
HRN_FORK_BEGIN(.timeout = 5000)
|
||||
{
|
||||
HRN_FORK_CHILD_BEGIN(.prefix = "sighup server")
|
||||
{
|
||||
// Ignore SIGHUP
|
||||
sigaction(SIGHUP, &(struct sigaction){.sa_handler = testSignalHandler}, NULL);
|
||||
|
||||
// Wait for connection. Use port 1 to avoid port conflicts later.
|
||||
IoServer *server = sckServerNew(STRDEF("127.0.0.1"), hrnServerPort(1), 5000);
|
||||
HRN_FORK_CHILD_NOTIFY_PUT();
|
||||
|
||||
TEST_RESULT_PTR(ioServerAccept(server, NULL), NULL, "connection interrupted");
|
||||
}
|
||||
HRN_FORK_CHILD_END();
|
||||
|
||||
HRN_FORK_PARENT_BEGIN(.prefix = "sighup client")
|
||||
{
|
||||
// Wait for client to be ready but also sleep a bit more to allow accept to initialize
|
||||
HRN_FORK_PARENT_NOTIFY_GET(0);
|
||||
sleep(1);
|
||||
|
||||
// Send SIGHUP and the client should exit
|
||||
kill(HRN_FORK_PROCESS_ID(0), SIGHUP);
|
||||
}
|
||||
HRN_FORK_PARENT_END();
|
||||
}
|
||||
HRN_FORK_END();
|
||||
}
|
||||
|
||||
// Additional coverage not provided by testing with actual certificates
|
||||
|
Loading…
Reference in New Issue
Block a user