From: Thomas Jarosch Date: Mon, 7 Apr 2008 16:31:41 +0000 (+0000) Subject: libi2ncommon: (tomj) revamped pid file handling, added unit test X-Git-Tag: v2.6~183 X-Git-Url: http://developer.intra2net.com/git/?a=commitdiff_plain;h=6bf07aed51816da514d81cb4d495de6403786fe5;p=libi2ncommon libi2ncommon: (tomj) revamped pid file handling, added unit test --- diff --git a/src/Makefile.am b/src/Makefile.am index 3dd6b92..6c8882a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,7 +5,7 @@ INCLUDES = -I$(top_srcdir)/src @LIBGETTEXT_CFLAGS@ @LIBICONV_CFLAGS@ $(all_inclu # the library search path. lib_LTLIBRARIES = libi2ncommon.la include_HEADERS = containerfunc.hpp daemonfunc.hxx filefunc.hxx \ - insocketstream.hxx ip_type.hxx ipfunc.hxx logread.hxx oftmpstream.hxx pidfile.hxx \ + insocketstream.hxx ip_type.hxx ipfunc.hxx logread.hxx oftmpstream.hxx pidfile.hpp \ pipestream.hxx stringfunc.hxx timefunc.hxx userfunc.hxx libi2ncommon_la_SOURCES = containerfunc.cpp daemonfunc.cpp filefunc.cpp \ ipfunc.cpp logread.cpp oftmpstream.cpp pidfile.cpp stringfunc.cpp timefunc.cpp \ @@ -16,4 +16,3 @@ libi2ncommon_la_SOURCES = containerfunc.cpp daemonfunc.cpp filefunc.cpp \ # example, the version is 2.1.2. (3:2:1) libi2ncommon_la_LIBADD = @LIBGETTEXT_LIBS@ @LIBICONV_LIBS@ -_SOURCES = containerfunc.hxx diff --git a/src/containerfunc.hxx b/src/containerfunc.hxx deleted file mode 100644 index 5b1314b..0000000 --- a/src/containerfunc.hxx +++ /dev/null @@ -1,3 +0,0 @@ -// for those who are used to .hxx files... - -#include "containerfunc.hpp" \ No newline at end of file diff --git a/src/pidfile.cpp b/src/pidfile.cpp index 6e63d75..c4bf1f2 100644 --- a/src/pidfile.cpp +++ b/src/pidfile.cpp @@ -1,111 +1,93 @@ -/*************************************************************************** - * Copyright (C) 2008 by Intra2net AG - Thomas Jarosch * - * thomas.jarosch@intra2net.com * - * http://www.intra2net.com * - ***************************************************************************/ -#include "pidfile.hxx" +/** + Pid file handling + + @copyright Intra2net AG + @license commerical +*/ +#include "pidfile.hpp" #include -#include + #include #include #include #include #include +#include "filefunc.hxx" +#include + using namespace std; +using namespace I2n; /** - * Constructor. Creates the pid file. If there is already a pid file - * with the same name it checks if the program is still alive. - * - * There are two ways to use this class: - * "Throwing mode" which throws an exception if another instance is already running - * or "non-throwing mode" where you have to check via is_already_running() if - * the pid file got created. In any case no pid file gets written if an instance is already running. - * Note: Don't use the throwing mode in a global variable. + * Constructor. * @param filename Full path to the pid file - * @param throw_exception Throw exception if another instance is already running - * @param unlink_file_on_destruction Remove pid file on destruction of the object. Only useful if running as root. + * @param remove_file_on_destruction Remove pid file on destruction of the object. Only useful if running as root. */ -pidfile::pidfile(const std::string &filename, bool throw_exception, bool unlink_file_on_destruction) +PidFile::PidFile(const std::string &filename, bool remove_file_on_destruction) { - this->filename = filename; - this->unlink_file_on_destruction = unlink_file_on_destruction; - - already_running = false; - already_running_pid = 0; - - if (!check_already_running(throw_exception)) { - ofstream of(filename.c_str()); - of << getpid() << endl; - } + Filename = filename; + RemoveFileOnDestruction = remove_file_on_destruction; + WroteFile = false; } /** - * Destructor. We delete the pid file if #unlink_file_on_destruction is set. + * Destructor. We delete the pid file if #RemoveFileOnDestruction is set + * and we actually wrote a file (#WroteFile). * This is only possible if we run with root priviledges as /var/run * is root writable only. */ -pidfile::~pidfile() +PidFile::~PidFile() { - if (unlink_file_on_destruction) { - unlink(filename.c_str()); - } + if (RemoveFileOnDestruction && WroteFile) + unlink(Filename); } /** - * Check if another instance is already running - * if the constructor is used in non-throwing mode. - * @return true if another instance is running, false otherwise + * Checks if the pid file for this program already exists. + * If yes it does a "kill" probe to see if it is alive. + * @param running_pid[out] Store pid number of instance if already running + * @return true if running, false otherweise */ -bool pidfile::is_already_running() +bool PidFile::check_already_running(pid_t *running_pid) { - return already_running; -} + // Check if there is an existing pid file + ifstream in(Filename.c_str()); + if (!in) + return false; -/** - * Get already running pid. See is_already_running(). - * @return pid of the already running instance or zero if not running. - */ -pid_t pidfile::get_already_running_pid() -{ - return already_running_pid; + pid_t read_pid = 0; + if (!(in >> read_pid)) + return false; + + // Ok, we got an existing pid file. + // Check if program is still alive + if (kill(read_pid, 0) == 0) + { + if (running_pid) + *running_pid = read_pid; + + // State: running + return true; + } + + // Default state: not running + return false; } /** - * Checks if the pid file for this program already exists. - * If yes it does a "kill" probe to see if it is alive. + * Write pid file + * @return true if file was written, false otherwise */ -bool pidfile::check_already_running(bool throw_exception) +bool PidFile::write() { - // Check if there is an existing pid file - ifstream in(filename.c_str()); - if (!in) { - return false; - } - - pid_t read_pid = 0; - if (!(in >> read_pid)) { - return false; - } - - // Ok, we got an existing pid file. - // Check if program is still alive - if (kill(read_pid, 0) == 0) { - already_running_pid = read_pid; - already_running = true; - - if (throw_exception) { - ostringstream out; - out << "Another instance running with pid " << read_pid << ". Aborting"; + ofstream out(Filename.c_str()); + if (!out) + return false; - throw runtime_error(out.str()); - } + out << getpid() << endl; - // State: running - return true; - } + WroteFile = true; - // Default state: not running - return false; + return true; } diff --git a/src/pidfile.hpp b/src/pidfile.hpp new file mode 100644 index 0000000..1941598 --- /dev/null +++ b/src/pidfile.hpp @@ -0,0 +1,30 @@ +#ifndef PIDFILE_H +#define PIDFILE_H + +#include +#include + +/// Pid file handling +/** + @copyright Intra2net AG + @license commerical +*/ +class PidFile +{ +public: + PidFile(const std::string &filename, bool remove_file_on_destruction=true); + ~PidFile(); + + bool check_already_running(pid_t *running_pid=NULL); + bool write(); + +private: + /// Full path to the pid file + std::string Filename; + /// Remove pid file on destruction + bool RemoveFileOnDestruction; + /// Indicates if we wrote the file (gets set by write()) + bool WroteFile; +}; + +#endif diff --git a/src/pidfile.hxx b/src/pidfile.hxx deleted file mode 100644 index f7c43a7..0000000 --- a/src/pidfile.hxx +++ /dev/null @@ -1,42 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008 by Intra2net AG - Thomas Jarosch * - * thomas.jarosch@intra2net.com * - * http://www.intra2net.com * - ***************************************************************************/ -#ifndef PIDFILE_H -#define PIDFILE_H - -#include -#include - -/// Pid file handling -/** - Creates a pid file in the constructor. - Optionally removes it in the destructor. - - @author Intra2net AG - Thomas Jarosch -*/ -class pidfile -{ -public: - pidfile(const std::string &filename, bool throw_exception=true, bool remove_file_on_destruction=true); - ~pidfile(); - - bool is_already_running(); - pid_t get_already_running_pid(); - -private: - bool check_already_running(bool throw_exception); - -private: - /// Full path to the pid file - std::string filename; - /// Remove pid file on destruction - bool unlink_file_on_destruction; - /// Indicates if the program is already running - bool already_running; - /// pid number of the already running instance. Default 0 - pid_t already_running_pid; -}; - -#endif diff --git a/test/Makefile.am b/test/Makefile.am index da12559..f3ef13a 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -2,7 +2,7 @@ INCLUDES = -I$(top_srcdir)/src @CPPUNIT_CFLAGS@ METASOURCES = AUTO check_PROGRAMS = test test_SOURCES = ip_range.cpp stringfunc.cpp test.cpp test_containerfunc.cpp \ - test_filefunc.cpp + test_filefunc.cpp test_pidfile.cpp test_LDADD = $(top_builddir)/src/libi2ncommon.la @CPPUNIT_LIBS@ TESTS = test diff --git a/test/test_pidfile.cpp b/test/test_pidfile.cpp new file mode 100644 index 0000000..477072d --- /dev/null +++ b/test/test_pidfile.cpp @@ -0,0 +1,112 @@ +/** + Pid file handling unit tests + + @copyright Intra2net AG + @license commerical +*/ + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +using namespace std; +using namespace CppUnit; + +class TestPidFile : public TestFixture +{ + CPPUNIT_TEST_SUITE(TestPidFile); + + CPPUNIT_TEST(WriteFile); + CPPUNIT_TEST(AutoRemoval); + CPPUNIT_TEST(NoAutoRemoval); + CPPUNIT_TEST(NotRunning); + CPPUNIT_TEST(IsAlreadyRunning); + + CPPUNIT_TEST_SUITE_END(); + +public: + void setUp() + { + ostringstream out; + out << "/var/run/test_pidfile.run." << getpid(); + + Filename = out.str(); + } + + void tearDown() + { + } + + void WriteFile() + { + PidFile pidfile(Filename); + bool rtn_write = pidfile.write(); + + CPPUNIT_ASSERT_EQUAL(rtn_write, true); + } + + void AutoRemoval() + { + bool rtn_write = false; + { + PidFile pidfile(Filename); + rtn_write = pidfile.write(); + } + bool exists = I2n::file_exists(Filename); + + CPPUNIT_ASSERT_EQUAL(rtn_write, true); + CPPUNIT_ASSERT_EQUAL(exists, false); + } + + void NoAutoRemoval() + { + bool rtn_write = false; + { + PidFile pidfile(Filename, false); + rtn_write = pidfile.write(); + } + bool exists = I2n::file_exists(Filename); + I2n::unlink(Filename); + + CPPUNIT_ASSERT_EQUAL(rtn_write, true); + CPPUNIT_ASSERT_EQUAL(exists, true); + } + + void NotRunning() + { + PidFile pidfile(Filename); + bool rtn_check = pidfile.check_already_running(); + + CPPUNIT_ASSERT_EQUAL(rtn_check, false); + } + + void IsAlreadyRunning() + { + PidFile pidfile1(Filename); + bool rtn_file1_check = pidfile1.check_already_running(); + bool rtn_file1_write = pidfile1.write(); + + CPPUNIT_ASSERT_EQUAL(rtn_file1_check, false); + CPPUNIT_ASSERT_EQUAL(rtn_file1_write, true); + + PidFile pidfile2(Filename); + bool rtn_file2_check = pidfile2.check_already_running(); + + CPPUNIT_ASSERT_EQUAL(rtn_file2_check, true); + } + +private: + string Filename; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestPidFile);