You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-09-16 09:06:18 +02:00
The start/stop commands are implemented entirely in C.
The Perl versions remain because they are still being used by the Perl stanza commands. Once the stanza commands are migrated they can be removed. Contributed by Cynthia Shang.
This commit is contained in:
committed by
David Steele
parent
fe196cb0df
commit
382ed92825
@@ -13,6 +13,17 @@
|
||||
|
||||
<release-list>
|
||||
<release date="XXXX-XX-XX" version="2.17dev" title="UNDER DEVELOPMENT">
|
||||
<release-core-list>
|
||||
<release-improvement-list>
|
||||
<release-item>
|
||||
<release-item-contributor-list>
|
||||
<release-item-contributor id="cynthia.shang"/>
|
||||
</release-item-contributor-list>
|
||||
|
||||
<p>The <cmd>start</cmd>/<cmd>stop</cmd> commands are implemented entirely in C.</p>
|
||||
</release-item>
|
||||
</release-improvement-list>
|
||||
</release-core-list>
|
||||
</release>
|
||||
|
||||
<release date="2019-08-05" version="2.16" title="C Migrations and Bug Fixes">
|
||||
|
@@ -117,17 +117,6 @@ sub main
|
||||
|
||||
$iResult = new pgBackRest::Check::Check()->process();
|
||||
}
|
||||
|
||||
# Process start/stop commands
|
||||
# --------------------------------------------------------------------------------------------------------------------------
|
||||
elsif (cfgCommandTest(CFGCMD_START))
|
||||
{
|
||||
lockStart();
|
||||
}
|
||||
elsif (cfgCommandTest(CFGCMD_STOP))
|
||||
{
|
||||
lockStop();
|
||||
}
|
||||
else
|
||||
{
|
||||
# Check that the repo path exists
|
||||
|
@@ -61,6 +61,8 @@ SRCS = \
|
||||
command/info/info.c \
|
||||
command/command.c \
|
||||
command/control/common.c \
|
||||
command/control/start.c \
|
||||
command/control/stop.c \
|
||||
command/local/local.c \
|
||||
command/restore/file.c \
|
||||
command/restore/protocol.c \
|
||||
@@ -244,6 +246,12 @@ command/command.o: command/command.c build.auto.h common/assert.h common/debug.h
|
||||
command/control/common.o: command/control/common.c build.auto.h command/control/common.h common/assert.h common/debug.h common/error.auto.h common/error.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c command/control/common.c -o command/control/common.o
|
||||
|
||||
command/control/start.o: command/control/start.c build.auto.h command/control/common.h common/assert.h common/debug.h common/error.auto.h common/error.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c command/control/start.c -o command/control/start.o
|
||||
|
||||
command/control/stop.o: command/control/stop.c build.auto.h command/control/common.h common/assert.h common/debug.h common/error.auto.h common/error.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/read.intern.h common/io/write.h common/io/write.intern.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h storage/helper.h storage/info.h storage/read.h storage/read.intern.h storage/storage.h storage/storage.intern.h storage/write.h storage/write.intern.h version.h
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c command/control/stop.c -o command/control/stop.o
|
||||
|
||||
command/expire/expire.o: command/expire/expire.c build.auto.h command/archive/common.h command/backup/common.h common/assert.h common/crypto/common.h common/debug.h common/error.auto.h common/error.h common/ini.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/regExp.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h info/info.h info/infoArchive.h info/infoBackup.h info/infoManifest.h info/infoPg.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c command/expire/expire.c -o command/expire/expire.o
|
||||
|
||||
@@ -457,7 +465,7 @@ info/infoManifest.o: info/infoManifest.c build.auto.h common/error.auto.h common
|
||||
info/infoPg.o: info/infoPg.c build.auto.h common/assert.h common/crypto/common.h common/debug.h common/error.auto.h common/error.h common/ini.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/log.h common/logLevel.h common/macro.h common/memContext.h common/object.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/json.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h info/info.h info/infoPg.h postgres/interface.h postgres/version.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c info/infoPg.c -o info/infoPg.o
|
||||
|
||||
main.o: main.c build.auto.h command/archive/get/get.h command/archive/push/push.h command/check/check.h command/command.h command/expire/expire.h command/help/help.h command/info/info.h command/local/local.h command/remote/remote.h command/storage/list.h common/assert.h common/debug.h common/error.auto.h common/error.h common/exit.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h config/load.h perl/exec.h postgres/interface.h version.h
|
||||
main.o: main.c build.auto.h command/archive/get/get.h command/archive/push/push.h command/check/check.h command/command.h command/control/start.h command/control/stop.h command/expire/expire.h command/help/help.h command/info/info.h command/local/local.h command/remote/remote.h command/storage/list.h common/assert.h common/debug.h common/error.auto.h common/error.h common/exit.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h config/load.h perl/exec.h postgres/interface.h version.h
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c main.c -o main.o
|
||||
|
||||
perl/config.o: perl/config.c build.auto.h common/assert.h common/debug.h common/error.auto.h common/error.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/json.h common/type/keyValue.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h
|
||||
|
@@ -18,7 +18,8 @@ lockStopFileName(const String *stanza)
|
||||
FUNCTION_TEST_PARAM(STRING, stanza);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
String *result = strNewFmt("%s/%s.stop", strPtr(cfgOptionStr(cfgOptLockPath)), stanza != NULL ? strPtr(stanza) : "all");
|
||||
String *result = strNewFmt(
|
||||
"%s/%s" STOP_FILE_EXT, strPtr(cfgOptionStr(cfgOptLockPath)), stanza != NULL ? strPtr(stanza) : "all");
|
||||
|
||||
FUNCTION_TEST_RETURN(result);
|
||||
}
|
||||
|
@@ -6,6 +6,11 @@ Common Handler for Control Commands
|
||||
|
||||
#include "common/type/string.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Constants
|
||||
***********************************************************************************************************************************/
|
||||
#define STOP_FILE_EXT ".stop"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
|
37
src/command/control/start.c
Normal file
37
src/command/control/start.c
Normal file
@@ -0,0 +1,37 @@
|
||||
/***********************************************************************************************************************************
|
||||
Start Command
|
||||
***********************************************************************************************************************************/
|
||||
#include "build.auto.h"
|
||||
|
||||
#include "command/control/common.h"
|
||||
#include "common/debug.h"
|
||||
#include "config/config.h"
|
||||
#include "storage/helper.h"
|
||||
#include "storage/storage.h"
|
||||
|
||||
void
|
||||
cmdStart(void)
|
||||
{
|
||||
FUNCTION_LOG_VOID(logLevelDebug);
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
// Remove the stop file so processes can run
|
||||
String *stopFile = lockStopFileName(cfgOptionStr(cfgOptStanza));
|
||||
|
||||
// If the stop file exists, then remove it
|
||||
if (storageExistsNP(storageLocal(), stopFile))
|
||||
{
|
||||
// If the file cannot be removed, storageRemove() will throw an error if the error is not ENOENT
|
||||
storageRemoveNP(storageLocalWrite(), stopFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_WARN("stop file does not exist%s",
|
||||
(cfgOptionTest(cfgOptStanza) ? strPtr(strNewFmt(" for stanza %s", strPtr(cfgOptionStr(cfgOptStanza)))) : ""));
|
||||
}
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
FUNCTION_LOG_RETURN_VOID();
|
||||
}
|
12
src/command/control/start.h
Normal file
12
src/command/control/start.h
Normal file
@@ -0,0 +1,12 @@
|
||||
/***********************************************************************************************************************************
|
||||
Start Command
|
||||
***********************************************************************************************************************************/
|
||||
#ifndef COMMAND_CONTROL_START_H
|
||||
#define COMMAND_CONTROL_START_H
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
void cmdStart(void);
|
||||
|
||||
#endif
|
108
src/command/control/stop.c
Normal file
108
src/command/control/stop.c
Normal file
@@ -0,0 +1,108 @@
|
||||
/***********************************************************************************************************************************
|
||||
Stop Command
|
||||
***********************************************************************************************************************************/
|
||||
#include "build.auto.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <sys/file.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "command/control/common.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/type/convert.h"
|
||||
#include "config/config.h"
|
||||
#include "storage/helper.h"
|
||||
#include "storage/storage.h"
|
||||
#include "storage/storage.intern.h"
|
||||
|
||||
void
|
||||
cmdStop(void)
|
||||
{
|
||||
FUNCTION_LOG_VOID(logLevelDebug);
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
String *stopFile = lockStopFileName(cfgOptionStr(cfgOptStanza));
|
||||
|
||||
// If the stop file does not already exist, then create it
|
||||
if (!storageExistsNP(storageLocal(), stopFile))
|
||||
{
|
||||
// Create the lock path (ignore if already created)
|
||||
storagePathCreateP(storageLocalWrite(), strPath(stopFile), .mode = 0770);
|
||||
|
||||
// Create the stop file with Read/Write and Create only - do not use Truncate
|
||||
int fileHandle = -1;
|
||||
THROW_ON_SYS_ERROR_FMT(
|
||||
((fileHandle = open(strPtr(stopFile), O_WRONLY | O_CREAT, STORAGE_MODE_FILE_DEFAULT)) == -1), FileOpenError,
|
||||
"unable to open stop file '%s'", strPtr(stopFile));
|
||||
|
||||
// Close the file
|
||||
close(fileHandle);
|
||||
|
||||
// If --force was specified then send term signals to running processes
|
||||
if (cfgOptionBool(cfgOptForce))
|
||||
{
|
||||
const String *lockPath = cfgOptionStr(cfgOptLockPath);
|
||||
StringList *lockPathFileList = strLstSort(
|
||||
storageListP(storageLocal(), lockPath, .errorOnMissing = true), sortOrderAsc);
|
||||
|
||||
// Find each lock file and send term signals to the processes
|
||||
for (unsigned int lockPathFileIdx = 0; lockPathFileIdx < strLstSize(lockPathFileList); lockPathFileIdx++)
|
||||
{
|
||||
String *lockFile = strNewFmt("%s/%s", strPtr(lockPath), strPtr(strLstGet(lockPathFileList, lockPathFileIdx)));
|
||||
|
||||
// Skip any file that is not a lock file
|
||||
if (!strEndsWithZ(lockFile, LOCK_FILE_EXT))
|
||||
continue;
|
||||
|
||||
// If we cannot open the lock file for any reason then warn and continue to next file
|
||||
if ((fileHandle = open(strPtr(lockFile), O_RDONLY, 0)) == -1)
|
||||
{
|
||||
LOG_WARN( "unable to open lock file %s", strPtr(lockFile));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Attempt a lock on the file - if a lock can be acquired that means the original process died without removing
|
||||
// the lock file so remove it now
|
||||
if (flock(fileHandle, LOCK_EX | LOCK_NB) == 0)
|
||||
{
|
||||
unlink(strPtr(lockFile));
|
||||
close(fileHandle);
|
||||
continue;
|
||||
}
|
||||
|
||||
// The file is locked so that means there is a running process - read the process id and send it a term signal
|
||||
char contents[64];
|
||||
ssize_t actualBytes = read(fileHandle, contents, sizeof(contents));
|
||||
String *processId = actualBytes > 0 ? strTrim(strNewN(contents, (size_t)actualBytes)) : NULL;
|
||||
|
||||
// If the process id is defined then assume this is a valid lock file
|
||||
if (processId != NULL && strSize(processId) > 0)
|
||||
{
|
||||
if (kill(cvtZToInt(strPtr(processId)), SIGTERM) != 0)
|
||||
LOG_WARN("unable to send term signal to process %s", strPtr(processId));
|
||||
else
|
||||
LOG_INFO("sent term signal to process %s", strPtr(processId));
|
||||
}
|
||||
else
|
||||
{
|
||||
unlink(strPtr(lockFile));
|
||||
close(fileHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_WARN(
|
||||
"stop file already exists for %s",
|
||||
cfgOptionTest(cfgOptStanza) ? strPtr(strNewFmt("stanza %s", strPtr(cfgOptionStr(cfgOptStanza)))) : "all stanzas");
|
||||
}
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
FUNCTION_LOG_RETURN_VOID();
|
||||
}
|
12
src/command/control/stop.h
Normal file
12
src/command/control/stop.h
Normal file
@@ -0,0 +1,12 @@
|
||||
/***********************************************************************************************************************************
|
||||
Stop Command
|
||||
***********************************************************************************************************************************/
|
||||
#ifndef COMMAND_CONTROL_STOP_H
|
||||
#define COMMAND_CONTROL_STOP_H
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
void cmdStop(void);
|
||||
|
||||
#endif
|
@@ -11,6 +11,8 @@ Main
|
||||
#include "command/archive/push/push.h"
|
||||
#include "command/check/check.h"
|
||||
#include "command/command.h"
|
||||
#include "command/control/start.h"
|
||||
#include "command/control/stop.h"
|
||||
#include "command/expire/expire.h"
|
||||
#include "command/help/help.h"
|
||||
#include "command/info/info.h"
|
||||
@@ -215,7 +217,7 @@ main(int argListSize, const char *argList[])
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
case cfgCmdStart:
|
||||
{
|
||||
perlExec();
|
||||
cmdStart();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -223,7 +225,7 @@ main(int argListSize, const char *argList[])
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
case cfgCmdStop:
|
||||
{
|
||||
perlExec();
|
||||
cmdStop();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@@ -8357,15 +8357,6 @@ static const EmbeddedModule embeddedModule[] =
|
||||
"\n"
|
||||
"$iResult = new pgBackRest::Check::Check()->process();\n"
|
||||
"}\n"
|
||||
"\n\n\n"
|
||||
"elsif (cfgCommandTest(CFGCMD_START))\n"
|
||||
"{\n"
|
||||
"lockStart();\n"
|
||||
"}\n"
|
||||
"elsif (cfgCommandTest(CFGCMD_STOP))\n"
|
||||
"{\n"
|
||||
"lockStop();\n"
|
||||
"}\n"
|
||||
"else\n"
|
||||
"{\n"
|
||||
"\n"
|
||||
|
@@ -627,10 +627,12 @@ unit:
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
- name: control
|
||||
total: 2
|
||||
total: 3
|
||||
|
||||
coverage:
|
||||
command/control/common: full
|
||||
command/control/start: full
|
||||
command/control/stop: full
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
- name: expire
|
||||
|
@@ -2,6 +2,9 @@
|
||||
Test Command Control
|
||||
***********************************************************************************************************************************/
|
||||
#include "common/harnessConfig.h"
|
||||
#include "common/harnessFork.h"
|
||||
#include "common/io/handleRead.h"
|
||||
#include "common/io/handleWrite.h"
|
||||
#include "storage/posix/storage.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
@@ -27,12 +30,12 @@ testRun(void)
|
||||
strLstAddZ(argList, "archive-get");
|
||||
harnessCfgLoad(strLstSize(argList), strLstPtr(argList));
|
||||
|
||||
TEST_RESULT_STR(strPtr(lockStopFileName(NULL)), "/path/to/lock/all.stop", "stop file for all stanzas");
|
||||
TEST_RESULT_STR(strPtr(lockStopFileName(strNew("db"))), "/path/to/lock/db.stop", "stop file for on stanza");
|
||||
TEST_RESULT_STR(strPtr(lockStopFileName(NULL)), "/path/to/lock/all" STOP_FILE_EXT, "stop file for all stanzas");
|
||||
TEST_RESULT_STR(strPtr(lockStopFileName(strNew("db"))), "/path/to/lock/db" STOP_FILE_EXT, "stop file for on stanza");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("lockStopTest()"))
|
||||
if (testBegin("lockStopTest(), cmdStart()"))
|
||||
{
|
||||
StringList *argList = strLstNew();
|
||||
strLstAddZ(argList, "pgbackrest");
|
||||
@@ -41,6 +44,12 @@ testRun(void)
|
||||
harnessCfgLoad(strLstSize(argList), strLstPtr(argList));
|
||||
|
||||
TEST_RESULT_VOID(lockStopTest(), "no stop files without stanza");
|
||||
TEST_RESULT_VOID(cmdStart(), " cmdStart - no stanza, no stop files");
|
||||
harnessLogResult("P00 WARN: stop file does not exist");
|
||||
|
||||
TEST_RESULT_VOID(storagePutNP(storageNewWriteNP(storageTest, strNew("all" STOP_FILE_EXT)), NULL), "create stop file");
|
||||
TEST_RESULT_VOID(cmdStart(), " cmdStart - no stanza, stop file exists");
|
||||
TEST_RESULT_BOOL(storageExistsNP(storageTest, strNew("all" STOP_FILE_EXT)), false, " stop file removed");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
argList = strLstNew();
|
||||
@@ -51,12 +60,308 @@ testRun(void)
|
||||
harnessCfgLoad(strLstSize(argList), strLstPtr(argList));
|
||||
|
||||
TEST_RESULT_VOID(lockStopTest(), "no stop files with stanza");
|
||||
TEST_RESULT_VOID(cmdStart(), " cmdStart - stanza, no stop files");
|
||||
harnessLogResult("P00 WARN: stop file does not exist for stanza db");
|
||||
|
||||
storagePutNP(storageNewWriteNP(storageTest, strNew("all.stop")), NULL);
|
||||
storagePutNP(storageNewWriteNP(storageTest, strNew("all" STOP_FILE_EXT)), NULL);
|
||||
TEST_ERROR(lockStopTest(), StopError, "stop file exists for all stanzas");
|
||||
|
||||
storagePutNP(storageNewWriteNP(storageTest, strNew("db.stop")), NULL);
|
||||
storagePutNP(storageNewWriteNP(storageTest, strNew("db" STOP_FILE_EXT)), NULL);
|
||||
TEST_ERROR(lockStopTest(), StopError, "stop file exists for stanza db");
|
||||
|
||||
TEST_RESULT_VOID(cmdStart(), "cmdStart - stanza, stop file exists");
|
||||
TEST_RESULT_BOOL(storageExistsNP(storageTest, strNew("db" STOP_FILE_EXT)), false, " stanza stop file removed");
|
||||
TEST_RESULT_BOOL(storageExistsNP(storageTest, strNew("all" STOP_FILE_EXT)), true, " all stop file not removed");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("cmdStop()"))
|
||||
{
|
||||
String *lockPath = strNewFmt("%s/lockpath", testPath());
|
||||
StringList *argList = strLstNew();
|
||||
strLstAddZ(argList, "pgbackrest");
|
||||
strLstAdd(argList, strNewFmt("--lock-path=%s", strPtr(lockPath)));
|
||||
strLstAddZ(argList, "stop");
|
||||
harnessCfgLoad(strLstSize(argList), strLstPtr(argList));
|
||||
|
||||
TEST_RESULT_VOID(cmdStop(), "no stanza, create stop file");
|
||||
StorageInfo info = {0};
|
||||
TEST_ASSIGN(info, storageInfoNP(storageTest, lockPath), " get path info");
|
||||
TEST_RESULT_INT(info.mode, 0770, " check path mode");
|
||||
TEST_RESULT_BOOL(
|
||||
storageExistsNP(storageTest, strNewFmt("%s/all" STOP_FILE_EXT, strPtr(lockPath))), true, " all stop file created");
|
||||
TEST_ASSIGN(info, storageInfoNP(storageTest, strNewFmt("%s/all" STOP_FILE_EXT, strPtr(lockPath))), " get file info");
|
||||
TEST_RESULT_INT(info.mode, 0640, " check file mode");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_RESULT_VOID(cmdStop(), "no stanza, stop file already exists");
|
||||
harnessLogResult("P00 WARN: stop file already exists for all stanzas");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_RESULT_VOID(storageRemoveNP(storageTest, strNew("lockpath/all" STOP_FILE_EXT)), "remove stop file");
|
||||
TEST_RESULT_INT(system(strPtr(strNewFmt("sudo chmod 444 %s", strPtr(lockPath)))), 0, "change perms");
|
||||
TEST_ERROR_FMT(
|
||||
cmdStop(), FileOpenError, "unable to stat '%s/all.stop': [13] Permission denied", strPtr(lockPath));
|
||||
TEST_RESULT_VOID(
|
||||
storagePathRemoveP(storageTest, lockPath, .recurse = true, .errorOnMissing = true), " remove the lock path");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
String *stanzaStopFile = strNewFmt("%s/db" STOP_FILE_EXT, strPtr(lockPath));
|
||||
strLstAddZ(argList, "--stanza=db");
|
||||
harnessCfgLoad(strLstSize(argList), strLstPtr(argList));
|
||||
|
||||
TEST_RESULT_VOID(cmdStop(), "stanza, create stop file");
|
||||
TEST_RESULT_BOOL(storageExistsNP(storageTest, stanzaStopFile), true, " stanza stop file created");
|
||||
|
||||
StringList *lockPathList = NULL;
|
||||
TEST_ASSIGN(lockPathList, storageListP(storageTest, strNew("lockpath"), .errorOnMissing = true), " get file list");
|
||||
TEST_RESULT_INT(strLstSize(lockPathList), 1, " only file in lock path");
|
||||
TEST_RESULT_STR(strPtr(strLstGet(lockPathList, 0)), "db" STOP_FILE_EXT, " stanza stop exists");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_RESULT_VOID(cmdStop(), "stanza, stop file already exists");
|
||||
harnessLogResult("P00 WARN: stop file already exists for stanza db");
|
||||
TEST_RESULT_VOID(storageRemoveNP(storageTest, stanzaStopFile), " remove stop file");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
strLstAddZ(argList, "--force");
|
||||
harnessCfgLoad(strLstSize(argList), strLstPtr(argList));
|
||||
TEST_RESULT_VOID(cmdStop(), "stanza, create stop file, force");
|
||||
TEST_RESULT_VOID(storageRemoveNP(storageTest, stanzaStopFile), " remove stop file");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_RESULT_VOID(
|
||||
storagePutNP(
|
||||
storageNewWriteP(storageTest, strNewFmt("%s/bad" LOCK_FILE_EXT, strPtr(lockPath)), .modeFile = 0222), NULL),
|
||||
"create a lock file that cannot be opened");
|
||||
TEST_RESULT_VOID(cmdStop(), " stanza, create stop file but unable to open lock file");
|
||||
harnessLogResult(strPtr(strNewFmt("P00 WARN: unable to open lock file %s/bad" LOCK_FILE_EXT, strPtr(lockPath))));
|
||||
TEST_RESULT_VOID(
|
||||
storagePathRemoveP(storageTest, lockPath, .recurse = true, .errorOnMissing = true), " remove the lock path");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_RESULT_VOID(
|
||||
storagePutNP(storageNewWriteNP(storageTest, strNewFmt("%s/empty" LOCK_FILE_EXT, strPtr(lockPath))), NULL),
|
||||
"create empty lock file");
|
||||
TEST_RESULT_VOID(cmdStop(), " stanza, create stop file, force - empty lock file");
|
||||
TEST_RESULT_BOOL(storageExistsNP(storageTest, stanzaStopFile), true, " stanza stop file created");
|
||||
TEST_RESULT_BOOL(
|
||||
storageExistsNP(storageTest, strNewFmt("%s/empty" LOCK_FILE_EXT, strPtr(lockPath))), false,
|
||||
" no other process lock, lock file was removed");
|
||||
|
||||
// empty lock file with another process lock, processId == NULL
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_RESULT_VOID(storageRemoveNP(storageTest, stanzaStopFile), "remove stop file");
|
||||
TEST_RESULT_VOID(
|
||||
storagePutNP(storageNewWriteNP(storageTest, strNewFmt("%s/empty" LOCK_FILE_EXT, strPtr(lockPath))), NULL),
|
||||
" create empty lock file");
|
||||
|
||||
HARNESS_FORK_BEGIN()
|
||||
{
|
||||
HARNESS_FORK_CHILD_BEGIN(0, true)
|
||||
{
|
||||
IoRead *read = ioHandleReadNew(strNew("child read"), HARNESS_FORK_CHILD_READ(), 2000);
|
||||
ioReadOpen(read);
|
||||
IoWrite *write = ioHandleWriteNew(strNew("child write"), HARNESS_FORK_CHILD_WRITE());
|
||||
ioWriteOpen(write);
|
||||
|
||||
int lockHandle = open(strPtr(strNewFmt("%s/empty" LOCK_FILE_EXT, strPtr(lockPath))), O_RDONLY, 0);
|
||||
TEST_RESULT_BOOL(lockHandle != -1, true, " file handle aquired");
|
||||
TEST_RESULT_INT(flock(lockHandle, LOCK_EX | LOCK_NB), 0, " lock the empty file");
|
||||
|
||||
// Let the parent know the lock has been acquired and wait for the parent to allow lock release
|
||||
ioWriteStrLine(write, strNew(""));
|
||||
// All writes are buffered so need to flush because buffer is not full
|
||||
ioWriteFlush(write);
|
||||
// Wait for a linefeed from the parent ioWriteLine below
|
||||
ioReadLine(read);
|
||||
|
||||
// Parent remove the file so just close the handle
|
||||
close(lockHandle);
|
||||
}
|
||||
HARNESS_FORK_CHILD_END();
|
||||
|
||||
HARNESS_FORK_PARENT_BEGIN()
|
||||
{
|
||||
IoRead *read = ioHandleReadNew(strNew("parent read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000);
|
||||
ioReadOpen(read);
|
||||
IoWrite *write = ioHandleWriteNew(strNew("parent write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0));
|
||||
ioWriteOpen(write);
|
||||
|
||||
// Wait for the child to acquire the lock
|
||||
ioReadLine(read);
|
||||
|
||||
TEST_RESULT_VOID(
|
||||
cmdStop(),
|
||||
" stanza, create stop file, force - empty lock file with another process lock, processId == NULL");
|
||||
TEST_RESULT_BOOL(
|
||||
storageExistsNP(storageTest, strNewFmt("%s/empty" LOCK_FILE_EXT, strPtr(lockPath))), false,
|
||||
" lock file was removed");
|
||||
|
||||
// Notify the child to release the lock
|
||||
ioWriteLine(write, bufNew(0));
|
||||
ioWriteFlush(write);
|
||||
}
|
||||
HARNESS_FORK_PARENT_END();
|
||||
}
|
||||
HARNESS_FORK_END();
|
||||
|
||||
// not empty lock file with another process lock, processId size trimmed to 0
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_RESULT_VOID(storageRemoveNP(storageTest, stanzaStopFile), "remove stop file");
|
||||
TEST_RESULT_VOID(
|
||||
storagePutNP(storageNewWriteNP(storageTest, strNewFmt("%s/empty" LOCK_FILE_EXT, strPtr(lockPath))), BUFSTRDEF(" ")),
|
||||
" create non-empty lock file");
|
||||
|
||||
HARNESS_FORK_BEGIN()
|
||||
{
|
||||
HARNESS_FORK_CHILD_BEGIN(0, true)
|
||||
{
|
||||
IoRead *read = ioHandleReadNew(strNew("child read"), HARNESS_FORK_CHILD_READ(), 2000);
|
||||
ioReadOpen(read);
|
||||
IoWrite *write = ioHandleWriteNew(strNew("child write"), HARNESS_FORK_CHILD_WRITE());
|
||||
ioWriteOpen(write);
|
||||
|
||||
int lockHandle = open(strPtr(strNewFmt("%s/empty" LOCK_FILE_EXT, strPtr(lockPath))), O_RDONLY, 0);
|
||||
TEST_RESULT_BOOL(lockHandle != -1, true, " file handle aquired");
|
||||
TEST_RESULT_INT(flock(lockHandle, LOCK_EX | LOCK_NB), 0, " lock the non-empty file");
|
||||
|
||||
// Let the parent know the lock has been acquired and wait for the parent to allow lock release
|
||||
ioWriteStrLine(write, strNew(""));
|
||||
// All writes are buffered so need to flush because buffer is not full
|
||||
ioWriteFlush(write);
|
||||
// Wait for a linefeed from the parent ioWriteLine below
|
||||
ioReadLine(read);
|
||||
|
||||
// Parent remove the file so just close the handle
|
||||
close(lockHandle);
|
||||
}
|
||||
HARNESS_FORK_CHILD_END();
|
||||
|
||||
HARNESS_FORK_PARENT_BEGIN()
|
||||
{
|
||||
IoRead *read = ioHandleReadNew(strNew("parent read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000);
|
||||
ioReadOpen(read);
|
||||
IoWrite *write = ioHandleWriteNew(strNew("parent write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0));
|
||||
ioWriteOpen(write);
|
||||
|
||||
// Wait for the child to acquire the lock
|
||||
ioReadLine(read);
|
||||
|
||||
TEST_RESULT_VOID(
|
||||
cmdStop(), " stanza, create stop file, force - empty lock file with another process lock, processId size 0");
|
||||
TEST_RESULT_BOOL(
|
||||
storageExistsNP(storageTest, strNewFmt("%s/empty" LOCK_FILE_EXT, strPtr(lockPath))), false,
|
||||
" lock file was removed");
|
||||
|
||||
// Notify the child to release the lock
|
||||
ioWriteLine(write, bufNew(0));
|
||||
ioWriteFlush(write);
|
||||
}
|
||||
HARNESS_FORK_PARENT_END();
|
||||
}
|
||||
HARNESS_FORK_END();
|
||||
|
||||
// lock file with another process lock, processId is valid
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_RESULT_VOID(storageRemoveNP(storageTest, stanzaStopFile), "remove stop file");
|
||||
HARNESS_FORK_BEGIN()
|
||||
{
|
||||
HARNESS_FORK_CHILD_BEGIN(0, true)
|
||||
{
|
||||
IoRead *read = ioHandleReadNew(strNew("child read"), HARNESS_FORK_CHILD_READ(), 2000);
|
||||
ioReadOpen(read);
|
||||
IoWrite *write = ioHandleWriteNew(strNew("child write"), HARNESS_FORK_CHILD_WRITE());
|
||||
ioWriteOpen(write);
|
||||
|
||||
TEST_RESULT_BOOL(
|
||||
lockAcquire(lockPath, cfgOptionStr(cfgOptStanza), 0, 30000, true), true," child process aquires lock");
|
||||
|
||||
// Let the parent know the lock has been acquired and wait for the parent to allow lock release
|
||||
ioWriteStrLine(write, strNew(""));
|
||||
// All writes are buffered so need to flush because buffer is not full
|
||||
ioWriteFlush(write);
|
||||
// Wait for a linefeed from the parent but it will not arrive before the process is terminated
|
||||
ioReadLine(read);
|
||||
}
|
||||
HARNESS_FORK_CHILD_END();
|
||||
|
||||
HARNESS_FORK_PARENT_BEGIN()
|
||||
{
|
||||
IoRead *read = ioHandleReadNew(strNew("parent read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000);
|
||||
ioReadOpen(read);
|
||||
IoWrite *write = ioHandleWriteNew(strNew("parent write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0));
|
||||
ioWriteOpen(write);
|
||||
|
||||
// Wait for the child to acquire the lock
|
||||
ioReadLine(read);
|
||||
|
||||
TEST_RESULT_VOID(
|
||||
cmdStop(),
|
||||
" stanza, create stop file, force - lock file with another process lock, processId is valid");
|
||||
|
||||
harnessLogResult(strPtr(strNewFmt("P00 INFO: sent term signal to process %d", HARNESS_FORK_PROCESS_ID(0))));
|
||||
}
|
||||
HARNESS_FORK_PARENT_END();
|
||||
}
|
||||
HARNESS_FORK_END();
|
||||
|
||||
// lock file with another process lock, processId is invalid
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_RESULT_VOID(storageRemoveNP(storageTest, stanzaStopFile), "remove stop file");
|
||||
TEST_RESULT_VOID(
|
||||
storagePutNP(
|
||||
storageNewWriteNP(storageTest, strNewFmt("%s/badpid" LOCK_FILE_EXT, strPtr(lockPath))), BUFSTRDEF("-32768")),
|
||||
"create lock file with invalid PID");
|
||||
|
||||
HARNESS_FORK_BEGIN()
|
||||
{
|
||||
HARNESS_FORK_CHILD_BEGIN(0, true)
|
||||
{
|
||||
IoRead *read = ioHandleReadNew(strNew("child read"), HARNESS_FORK_CHILD_READ(), 2000);
|
||||
ioReadOpen(read);
|
||||
IoWrite *write = ioHandleWriteNew(strNew("child write"), HARNESS_FORK_CHILD_WRITE());
|
||||
ioWriteOpen(write);
|
||||
|
||||
int lockHandle = open(strPtr(strNewFmt("%s/badpid" LOCK_FILE_EXT, strPtr(lockPath))), O_RDONLY, 0);
|
||||
TEST_RESULT_BOOL(lockHandle != -1, true, " file handle aquired");
|
||||
TEST_RESULT_INT(flock(lockHandle, LOCK_EX | LOCK_NB), 0, " lock the badpid file");
|
||||
|
||||
// Let the parent know the lock has been acquired and wait for the parent to allow lock release
|
||||
ioWriteStrLine(write, strNew(""));
|
||||
// All writes are buffered so need to flush because buffer is not full
|
||||
ioWriteFlush(write);
|
||||
// Wait for a linefeed from the parent ioWriteLine below
|
||||
ioReadLine(read);
|
||||
|
||||
// Remove the file and close the handle
|
||||
storageRemoveNP(storageTest, strNewFmt("%s/badpid" LOCK_FILE_EXT, strPtr(lockPath)));
|
||||
close(lockHandle);
|
||||
}
|
||||
HARNESS_FORK_CHILD_END();
|
||||
|
||||
HARNESS_FORK_PARENT_BEGIN()
|
||||
{
|
||||
IoRead *read = ioHandleReadNew(strNew("parent read"), HARNESS_FORK_PARENT_READ_PROCESS(0), 2000);
|
||||
ioReadOpen(read);
|
||||
IoWrite *write = ioHandleWriteNew(strNew("parent write"), HARNESS_FORK_PARENT_WRITE_PROCESS(0));
|
||||
ioWriteOpen(write);
|
||||
|
||||
// Wait for the child to acquire the lock
|
||||
ioReadLine(read);
|
||||
|
||||
TEST_RESULT_VOID(
|
||||
cmdStop(), " stanza, create stop file, force - lock file with another process lock, processId is invalid");
|
||||
harnessLogResult("P00 WARN: unable to send term signal to process -32768");
|
||||
TEST_RESULT_BOOL(storageExistsNP(storageTest, stanzaStopFile), true, " stanza stop file not removed");
|
||||
|
||||
// Notify the child to release the lock
|
||||
ioWriteLine(write, bufNew(0));
|
||||
ioWriteFlush(write);
|
||||
}
|
||||
HARNESS_FORK_PARENT_END();
|
||||
}
|
||||
HARNESS_FORK_END();
|
||||
}
|
||||
|
||||
FUNCTION_HARNESS_RESULT_VOID();
|
||||
|
Reference in New Issue
Block a user