diff --git a/.gitignore b/.gitignore index 42412a4..a9c95f5 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ SocketsTest TimevalTest URLEncodeTest VectorTest +SelfDetectTest +UnixSignalTest diff --git a/Exit.h b/Exit.h new file mode 100644 index 0000000..a9f6de9 --- /dev/null +++ b/Exit.h @@ -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 +#include +#include + +#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 diff --git a/Makefile.am b/Makefile.am index f253cd7..b803a94 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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) diff --git a/SelfDetect.cpp b/SelfDetect.cpp new file mode 100644 index 0000000..5c81757 --- /dev/null +++ b/SelfDetect.cpp @@ -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 . + +*/ + +#include +#include +#include +#include +#include +#include +#include + +#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::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); +} + diff --git a/SelfDetect.h b/SelfDetect.h new file mode 100644 index 0000000..ce9fb8c --- /dev/null +++ b/SelfDetect.h @@ -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 +#include +#include +#include + +/** 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 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 diff --git a/SelfDetectTest.cpp b/SelfDetectTest.cpp new file mode 100644 index 0000000..aa66b23 --- /dev/null +++ b/SelfDetectTest.cpp @@ -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 . +*/ + +#include +#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; +} diff --git a/UnixSignal.cpp b/UnixSignal.cpp new file mode 100644 index 0000000..e6950b0 --- /dev/null +++ b/UnixSignal.cpp @@ -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 . + +*/ + +#include +#include +#include +#include +#include +#include +#include + +#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::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::iterator i = mListHandlers[sig].begin(); + i != mListHandlers[sig].end(); i++) + { + printf("%s0x%p", i == mListHandlers[sig].begin() ? "" : ", ", *i); + } + printf("\n"); + } + mLock[sig].unlock(); + } +} diff --git a/UnixSignal.h b/UnixSignal.h new file mode 100644 index 0000000..eee1675 --- /dev/null +++ b/UnixSignal.h @@ -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 +#include +#include + +#include "Threads.h" + +/** A C++ wrapper for managing unix signals. */ +class UnixSignal +{ +public: + static const int C_NSIG = 64; +private: + std::list 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 diff --git a/UnixSignalTest.cpp b/UnixSignalTest.cpp new file mode 100644 index 0000000..83c45c6 --- /dev/null +++ b/UnixSignalTest.cpp @@ -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 . +*/ + +#include +#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; +} diff --git a/Variables.cpp b/Variables.cpp new file mode 100644 index 0000000..5c6624d --- /dev/null +++ b/Variables.cpp @@ -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;