libi2ncommon: (tomj) revamped pid file handling, added unit test
authorThomas Jarosch <thomas.jarosch@intra2net.com>
Mon, 7 Apr 2008 16:31:41 +0000 (16:31 +0000)
committerThomas Jarosch <thomas.jarosch@intra2net.com>
Mon, 7 Apr 2008 16:31:41 +0000 (16:31 +0000)
src/Makefile.am
src/containerfunc.hxx [deleted file]
src/pidfile.cpp
src/pidfile.hpp [new file with mode: 0644]
src/pidfile.hxx [deleted file]
test/Makefile.am
test/test_pidfile.cpp [new file with mode: 0644]

index 3dd6b92..6c8882a 100644 (file)
@@ -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 (file)
index 5b1314b..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-// for those who are used to .hxx files...
-
-#include "containerfunc.hpp"
\ No newline at end of file
index 6e63d75..c4bf1f2 100644 (file)
-/***************************************************************************
- *   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 <sys/types.h>
-#include <unistd.h>
+
 #include <signal.h>
 #include <fstream>
 #include <sstream>
 #include <iostream>
 #include <stdexcept>
 
+#include "filefunc.hxx"
+#include <unistd.h>
+
 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 (file)
index 0000000..1941598
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef PIDFILE_H
+#define PIDFILE_H
+
+#include <string>
+#include <sys/types.h>
+
+/// 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 (file)
index f7c43a7..0000000
+++ /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 <string>
-#include <sys/types.h>
-
-/// Pid file handling
-/**
-    Creates a pid file in the constructor.
-    Optionally removes it in the destructor.
-
-    @author Intra2net AG - Thomas Jarosch <thomas.jarosch@intra2net.com>
-*/
-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
index da12559..f3ef13a 100644 (file)
@@ -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 (file)
index 0000000..477072d
--- /dev/null
@@ -0,0 +1,112 @@
+/**
+   Pid file handling unit tests
+
+   @copyright Intra2net AG
+   @license commerical
+*/
+
+#include <unistd.h>
+
+#include <iostream>
+#include <string>
+#include <sstream>
+#include <stdexcept>
+
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <cppunit/ui/text/TestRunner.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+#include <pidfile.hpp>
+#include <filefunc.hxx>
+
+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);