1
0
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:
Cynthia Shang
2019-08-09 15:17:18 -04:00
committed by David Steele
parent fe196cb0df
commit 382ed92825
13 changed files with 513 additions and 30 deletions

View File

@@ -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">

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}

View File

@@ -6,6 +6,11 @@ Common Handler for Control Commands
#include "common/type/string.h"
/***********************************************************************************************************************************
Constants
***********************************************************************************************************************************/
#define STOP_FILE_EXT ".stop"
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/

View 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();
}

View 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
View 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();
}

View File

@@ -0,0 +1,12 @@
/***********************************************************************************************************************************
Stop Command
***********************************************************************************************************************************/
#ifndef COMMAND_CONTROL_STOP_H
#define COMMAND_CONTROL_STOP_H
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
void cmdStop(void);
#endif

View File

@@ -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;
}

View File

@@ -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"

View File

@@ -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

View File

@@ -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();