From 414179aa825e86ea9c9f49532e397bf584eb5085 Mon Sep 17 00:00:00 2001 From: Michael Iedema Date: Mon, 31 Mar 2014 10:59:57 +0200 Subject: [PATCH] - 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 --- .gitignore | 2 + Exit.h | 60 ++++++++++++++++++++ Makefile.am | 14 +++++ SelfDetect.cpp | 133 +++++++++++++++++++++++++++++++++++++++++++++ SelfDetect.h | 55 +++++++++++++++++++ SelfDetectTest.cpp | 70 ++++++++++++++++++++++++ UnixSignal.cpp | 117 +++++++++++++++++++++++++++++++++++++++ UnixSignal.h | 45 +++++++++++++++ UnixSignalTest.cpp | 74 +++++++++++++++++++++++++ Variables.cpp | 24 ++++++++ 10 files changed, 594 insertions(+) create mode 100644 Exit.h create mode 100644 SelfDetect.cpp create mode 100644 SelfDetect.h create mode 100644 SelfDetectTest.cpp create mode 100644 UnixSignal.cpp create mode 100644 UnixSignal.h create mode 100644 UnixSignalTest.cpp create mode 100644 Variables.cpp 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;