- merging in Dave's selfDetect branch so we can limit our components to only a single instance

- small changes to log messages, passing signal information through to the log and also echoing failed starts to to cout
- a HACK was needed to avoid signal 17, Dave can smooth this out in a more intelligent way than my workaround
- ticket #1549
This commit is contained in:
Michael Iedema 2014-03-31 10:59:57 +02:00
parent 4575b4d264
commit 414179aa82
10 changed files with 594 additions and 0 deletions

2
.gitignore vendored
View File

@ -18,3 +18,5 @@ SocketsTest
TimevalTest
URLEncodeTest
VectorTest
SelfDetectTest
UnixSignalTest

60
Exit.h Normal file
View File

@ -0,0 +1,60 @@
/*
* Copyright 2014 Range Networks, Inc.
* All Rights Reserved.
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribuion.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef EXIT_H
#define EXIT_H
#include <list>
#include <sys/types.h>
#include <signal.h>
#include "Threads.h"
/** A C++ wrapper for managing exit codes. Note that this is a helper class
* without implementation.
*/
class Exit
{
public:
enum eCodes
{
SUCCESS = 0, // no error
DETECTFILE, // Unable to detect a required file: CommonLibs SelfDetect
CREATEFILE, // Unable to create a required file: CommonLibs SelfDetect
// Put all error codes above this line, to prevent error number values
// from changing, put after the previous enum value (ie, preserve
// the order. Also, provide a comment indicating which program(s) and
// the purpose.
LIMIT = 256 // all error codes must be less than this
};
private:
inline Exit(void) { /* cannot be constructed */; };
inline ~Exit(void) { /* cannot be destructed */; };
public:
inline static void exit(int status)
{
if (status < SUCCESS || status >= LIMIT)
{
printf("INFO: Unsupported exit status of %d\n", status);
}
::exit(status);
/*NOTREACHED*/
}
};
#endif // EXIT_H
// vim: ts=4 sw=4

View File

@ -31,8 +31,11 @@ EXTRA_DIST = \
noinst_LTLIBRARIES = libcommon.la
libcommon_la_SOURCES = \
Variables.cpp \
BitVector.cpp \
LinkedLists.cpp \
SelfDetect.cpp \
UnixSignal.cpp \
Sockets.cpp \
Threads.cpp \
Timeval.cpp \
@ -46,6 +49,8 @@ libcommon_la_SOURCES = \
noinst_PROGRAMS = \
BitVectorTest \
InterthreadTest \
SelfDetectTest \
UnixSignalTest \
SocketsTest \
TimevalTest \
RegexpTest \
@ -62,6 +67,9 @@ noinst_HEADERS = \
BitVector.h \
Interthread.h \
LinkedLists.h \
SelfDetect.h \
UnixSignal.h \
Exit.h \
Sockets.h \
Threads.h \
Timeval.h \
@ -101,6 +109,12 @@ RegexpTest_LDADD = libcommon.la
ConfigurationTest_SOURCES = ConfigurationTest.cpp
ConfigurationTest_LDADD = libcommon.la $(SQLITE_LA)
SelfDetectTest_SOURCES = SelfDetectTest.cpp
SelfDetectTest_LDADD = libcommon.la $(SQLITE_LA)
UnixSignalTest_SOURCES = UnixSignalTest.cpp
UnixSignalTest_LDADD = libcommon.la $(SQLITE_LA)
# ReportingTest_SOURCES = ReportingTest.cpp
# ReportingTest_LDADD = libcommon.la $(SQLITE_LA)

133
SelfDetect.cpp Normal file
View File

@ -0,0 +1,133 @@
/**@file Module for preventing two instances of a program from running. */
/*
* Copyright 2013 Range Networks, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cerrno>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "UnixSignal.h"
#include "SelfDetect.h"
#include "Logger.h"
#include "Exit.h"
//SelfDetect gSelf;
static void e(void)
{
gSelf.Exit(-999);
}
static void sigfcn(int sig)
{
gSelf.Exit(sig);
}
void SelfDetect::RegisterProgram(const char *argv0)
{
const char *p = argv0 + strlen(argv0); // point to end of string
while (p > argv0 && *p != '/') p--;
if (*p == '/') p++;
char buf[BUFSIZ];
snprintf(buf, sizeof(buf)-1, "/var/run/%s.pid", p);
LOG(NOTICE) << "*** Registering program " << argv0 << " to " << buf;
// first, verify we aren't already running.
struct stat stbuf;
if (stat(buf, &stbuf) >= 0)
{
LOG(ERR) << "*** An instance of " << p << " is already running. ";
LOG(ERR) << "*** If this is not the case, deleting this file will allow " << p << " to start: " << buf;
std::cout << "*** An instance of " << p << " is already running. " << std::endl;
std::cout << "*** If this is not the case, deleting this file will allow " << p << " to start: " << buf << std::endl;
Exit::exit(Exit::DETECTFILE);
}
FILE *fp = fopen(buf, "w");
if (fp == NULL)
{
LOG(ERR) << "*** Unable to create " << buf << ": " << strerror(errno);
Exit::exit(Exit::CREATEFILE);
}
fprintf(fp, "%d\n", getpid());
fclose(fp);
atexit(e);
// Now, register for all signals to do the cleanup
for (int i = 1; i < UnixSignal::C_NSIG; i++)
{
// HACK because a signal 17 is thrown during a correct initiation of OpenBTS and transceiver
// openbts: ALERT 3073816320 05:03:50.4 OpenBTS.cpp:491:main: starting the transceiver
// openbts: NOTICE 3073816320 05:03:50.4 SelfDetect.cpp:91:Exit: *** Terminating because of signal 17
// openbts: NOTICE 3031243584 05:03:50.4 OpenBTS.cpp:165:startTransceiver: starting transceiver ./transceiver w/ 1 ARFCNs and Args:
// openbts: NOTICE 3073816320 05:03:50.4 SelfDetect.cpp:98:Exit: *** Terminating ./OpenBTS
// openbts: NOTICE 3073816320 05:03:50.4 SelfDetect.cpp:105:Exit: *** Removing pid file /var/run/OpenBTS.pid
if (i == 17) {
continue;
}
gSigVec.Register(sigfcn, i);
}
mProg = strdup(argv0);
mFile = strdup(buf);
}
void SelfDetect::Exit(int sig)
{
LOG(NOTICE) << "*** Terminating because of signal " << sig;
if (mProg == NULL)
{
LOG(NOTICE) << "*** Terminating without registration of program";
} else
{
LOG(NOTICE) << "*** Terminating " << mProg;
}
if (mFile == NULL)
{
LOG(NOTICE) << "*** Terminating without pid file";
} else
{
LOG(NOTICE) << "*** Removing pid file " << mFile;
unlink(mFile);
}
for (std::list<std::string>::iterator i = mListFiles.begin();
i != mListFiles.end(); ++i)
{
LOG(NOTICE) << "*** Removing " << i->c_str();
unlink(i->c_str());
}
}
void SelfDetect::RegisterFile(const char *file)
{
LOG(NOTICE) << "*** Registering " << file << " for removal at program exit";
std::string s(file);
mListFiles.push_back(s);
}

55
SelfDetect.h Normal file
View File

@ -0,0 +1,55 @@
/*
* Copyright 2013 Range Networks, Inc.
* All Rights Reserved.
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribuion.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef SELFDETECT_H
#define SELFDETECT_H
#include <list>
#include <string>
#include <cstdlib>
#include <cstdio>
/** A C++ wrapper for preventing 2 instances of a program from running. */
class SelfDetect
{
private:
const char *mProg; // program name, which will map to /tmp/%s.pid
const char *mFile; // file in /tmp for pid
std::list<std::string> mListFiles; // list of files to be removed at shutdown
public:
SelfDetect(void)
{
mProg = NULL;
mFile = NULL;
mListFiles.clear();
}
~SelfDetect(void)
{
if (mProg) { free((void *)mProg); mProg = NULL; }
if (mFile) { free((void *)mFile); mFile = NULL; }
mListFiles.clear();
}
void RegisterProgram(const char *argv0); // register the program and validate
void RegisterFile(const char *file); // register a file to be removed at shutdown / exit
void Exit(int); // atexit() registration function -- called internally by real atexit() function that isn't in a class
};
extern SelfDetect gSelf;
#endif // SELFDETECT_H
// vim: ts=4 sw=4

70
SelfDetectTest.cpp Normal file
View File

@ -0,0 +1,70 @@
/*
* Copyright 2013 Range Networks, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstdlib>
#include "SelfDetect.h"
#include "Configuration.h"
#include "Logger.h"
ConfigurationTable gConfig;
// For best effects, run this program thrice: once with a pre-created
// /var/run/argv.pid, and once without. First one should exit with an
// error. Second should run this test. Third, with an argument, then type
// Control-C.
int main(int argc, char *argv[])
{
int ret;
gLogInit("SelfDetectTest","DEBUG",LOG_LOCAL7);
gSelf.RegisterProgram(argv[0]);
ret = system("touch /tmp/foo.1");
ret = system("touch /tmp/foo.2");
ret = system("touch /tmp/foo.3");
ret = system("touch /tmp/foo.4");
ret = system("touch /tmp/foo.5");
ret = system("touch /tmp/foo.6");
ret = system("touch /tmp/foo.7");
if (ret < 0) { ret = ret; } // warning eater
gSelf.RegisterFile("/tmp/foo.0"); // ignored
gSelf.RegisterFile("/tmp/foo.1"); // removed
gSelf.RegisterFile("/tmp/foo.2"); // removed
gSelf.RegisterFile("/tmp/foo.3"); // removed
gSelf.RegisterFile("/tmp/foo.4"); // removed
gSelf.RegisterFile("/tmp/foo.5"); // removed
gSelf.RegisterFile("/tmp/foo.6"); // removed
gSelf.RegisterFile("/tmp/foo.7"); // removed
gSelf.RegisterFile("/tmp/foo.8"); // ignored
gSelf.RegisterFile("/tmp/foo.9"); // ignored
if (argv[1] != NULL)
{
printf("Use a Control-C to test in this mode, make sure\n");
printf("that the file goes away\n");
while(1)
sleep(60);
}
return 0;
}

117
UnixSignal.cpp Normal file
View File

@ -0,0 +1,117 @@
/**@file Module for managing Linux signals and allowing multiple handlers per signal. */
/*
* Copyright 2014 Range Networks, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cerrno>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "UnixSignal.h"
#include "Logger.h"
//UnixSignal gSigVec;
static void _sigHandler(int sig)
{
signal(sig, SIG_IGN);
if (sig <= 0 || sig >= UnixSignal::C_NSIG)
{
LOG(ERR) << "Signal Handler for signal " << sig << " (out of range)";
return;
}
// work around C++ issue with function pointers and class based function pointers
gSigVec.Handler(sig);
signal(sig, SIG_DFL);
printf("Rethrowing signal %d\n", sig);
kill(getpid(), sig);
}
void UnixSignal::Handler(int sig)
{
//TODO: Code should go here to generate the core file, before we change too
// much of the data in the program.
printf("Processing signal vector for sig %d\n", sig);
mLock[sig].lock();
for (std::list<sighandler_t>::iterator i = mListHandlers[sig].begin();
i != mListHandlers[sig].end(); i++)
{
(*i)(sig);
}
mLock[sig].unlock();
printf("Done processing signal vector for sig %d\n", sig);
}
UnixSignal::UnixSignal(void)
{
for (int i = 0; i < C_NSIG; i++)
{
mListHandlers[i].clear();
signal(i, _sigHandler);
}
}
UnixSignal::~UnixSignal(void)
{
for (int i = 0; i < C_NSIG; i++)
{
mListHandlers[i].clear();
signal(i, SIG_DFL);
}
}
void UnixSignal::Register(sighandler_t handler, int sig) // register the handler to the signal
{
if (sig <= 0 || sig >= C_NSIG)
{
LOG(ERR) << "Unable to register callback for UnixSignal " << sig << " (out of range)";
return;
}
mLock[sig].lock();
mListHandlers[sig].insert(mListHandlers[sig].end(), handler);
mLock[sig].unlock();
}
void UnixSignal::Dump(void)
{
for (int sig = 0; sig < C_NSIG; sig++)
{
mLock[sig].lock();
if (mListHandlers[sig].size() != 0)
{
printf("Signal vectors for signal %d: ", sig);
for (std::list<sighandler_t>::iterator i = mListHandlers[sig].begin();
i != mListHandlers[sig].end(); i++)
{
printf("%s0x%p", i == mListHandlers[sig].begin() ? "" : ", ", *i);
}
printf("\n");
}
mLock[sig].unlock();
}
}

45
UnixSignal.h Normal file
View File

@ -0,0 +1,45 @@
/*
* Copyright 2014 Range Networks, Inc.
* All Rights Reserved.
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribuion.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef UNIXSIGNAL_H
#define UNIXSIGNAL_H
#include <list>
#include <sys/types.h>
#include <signal.h>
#include "Threads.h"
/** A C++ wrapper for managing unix signals. */
class UnixSignal
{
public:
static const int C_NSIG = 64;
private:
std::list<sighandler_t> mListHandlers[C_NSIG]; // list of signal handlers to call for all signals
Mutex mLock[C_NSIG];
public:
UnixSignal(void);
~UnixSignal(void);
void Register(sighandler_t handler, int sig); // register a signal handler with the system
void Handler(int sig); // main signal handler, iterates through mListHandlers
void Dump(void); // debug dump of list
};
extern UnixSignal gSigVec;
#endif // UNIXSIGNAL_H
// vim: ts=4 sw=4

74
UnixSignalTest.cpp Normal file
View File

@ -0,0 +1,74 @@
/*
* Copyright 2014 Range Networks, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstdlib>
#include "UnixSignal.h"
#include "Configuration.h"
#include "Logger.h"
ConfigurationTable gConfig;
void test1(int sig) { printf("Test 1, signal %d\n", sig); }
void test2(int sig) { printf("Test 2, signal %d\n", sig); }
void test3(int sig) { printf("Test 3, signal %d\n", sig); }
void test4(int sig) { printf("Test 4, signal %d\n", sig); }
void test5(int sig) { printf("Test 5, signal %d\n", sig); }
void test6(int sig) { printf("Test 6, signal %d\n", sig); }
void registerFuncs(int sig)
{
gSigVec.Register(test1, sig);
gSigVec.Register(test2, sig);
gSigVec.Register(test3, sig);
gSigVec.Register(test4, sig);
gSigVec.Register(test5, sig);
gSigVec.Register(test6, sig);
}
int main(int argc, char *argv[])
{
gLogInit("UnixSignalTest","DEBUG",LOG_LOCAL7);
registerFuncs(SIGINT);
registerFuncs(SIGHUP);
registerFuncs(SIGTERM);
printf("Test1 @ %p\n", test1);
printf("Test2 @ %p\n", test2);
printf("Test3 @ %p\n", test3);
printf("Test4 @ %p\n", test4);
printf("Test5 @ %p\n", test5);
printf("Test6 @ %p\n", test6);
printf("\n");
gSigVec.Dump();
printf("Run this three times. First time, do a 'kill %d' to test SIGTERM\n", getpid());
printf("Second time, do a 'kill -%d %d' to test SIGHUP\n", SIGHUP, getpid());
printf("Third time, do a Control-C to test SIGINT\n");
printf("\n");
printf("Output should iterate through tests 1-6 with the correct signal number\n");
printf("%d for SIGTERM, %d for SIGHUP, %d for SIGINT\n",
SIGTERM, SIGHUP, SIGINT);
while(true)
sleep(60);
return 0;
}

24
Variables.cpp Normal file
View File

@ -0,0 +1,24 @@
/*
* Copyright 2014 Range Networks, Inc.
* All Rights Reserved.
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribuion.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#include "UnixSignal.h"
#include "SelfDetect.h"
// These variables all have constructors, and their may be some interaction
// between them, so the order of construction is important.
UnixSignal gSigVec;
SelfDetect gSelf;