add DbMgr

Change-Id: I799f6a066ef60ecd42d64de7d8d04be8996564ed
diff --git a/.waf-tools/default-compiler-flags.py b/.waf-tools/default-compiler-flags.py
index 97921e2..249ddcd 100644
--- a/.waf-tools/default-compiler-flags.py
+++ b/.waf-tools/default-compiler-flags.py
@@ -21,7 +21,7 @@
     else:
         defaultFlags += ['-std=c++03', '-Wno-variadic-macros', '-Wno-c99-extensions']
 
-    defaultFlags += ['-pedantic', '-Wall', '-Wno-long-long', '-Wno-unneeded-internal-declaration']
+    defaultFlags += ['-Wall', '-Wno-long-long', '-Wno-unneeded-internal-declaration']
 
     if conf.options.debug:
         conf.define('_DEBUG', 1)
diff --git a/docs/index.rst b/docs/index.rst
index 052856e..9c11ec6 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -16,6 +16,7 @@
 
    INSTALL
    ndns-packet-format
+   ndns-db-manage
    manpages
 
 **Additional documentation**
diff --git a/docs/ndns-db-manage.rst b/docs/ndns-db-manage.rst
new file mode 100644
index 0000000..84c5abb
--- /dev/null
+++ b/docs/ndns-db-manage.rst
@@ -0,0 +1,21 @@
+Database Management Hints
+=========================
+
+Pre-requisites
+--------------
+
+``sqlite3`` installed
+
+
+Create the database
+-------------------
+
+Set the attribute ``dbfile`` in the NDNS configuration file, e.g., ``${SYSCONFDIR}/ndn/ndns.conf`` (``/etc/ndn/ndns.conf``), to the desired path of the database file.
+By default, ``dbfile`` is assigned to ``${LOCALSTATEDIR}/lib/ndns/ndns.db`` (``/var/lib/ndns/ndns.db``).
+
+When NDNS started, an empty database will be automatically created.
+
+Reset the database
+------------------
+
+Remove the database file.
diff --git a/src/db/db-mgr.cpp b/src/db/db-mgr.cpp
new file mode 100644
index 0000000..27d7db4
--- /dev/null
+++ b/src/db/db-mgr.cpp
@@ -0,0 +1,112 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014, Regents of the University of California.
+ *
+ * This file is part of NDNS (Named Data Networking Domain Name Service).
+ * See AUTHORS.md for complete list of NDNS authors and contributors.
+ *
+ * NDNS is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NDNS 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NDNS, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "db-mgr.hpp"
+#include "../logger.hpp"
+
+#include <boost/algorithm/string/predicate.hpp>
+
+#include <iostream>
+#include <fstream>
+
+namespace ndn {
+namespace ndns {
+
+NDNS_LOG_INIT("DbMgr");
+
+static const std::string NDNS_SCHEMA = "\
+CREATE TABLE IF NOT EXISTS zones (      \n\
+  id    INTEGER NOT NULL PRIMARY KEY,   \n\
+  name  blob NOT NULL UNIQUE,           \n\
+  ttl   integer(10) NOT NULL);          \n\
+                                        \n\
+CREATE TABLE IF NOT EXISTS rrsets (     \n\
+  id      INTEGER NOT NULL PRIMARY KEY, \n\
+  zone_id integer(10) NOT NULL,         \n\
+  label   blob NOT NULL,                \n\
+  type    blob NOT NULL,                \n\
+  version blob NOT NULL,                \n\
+  ttl     integer(10) NOT NULL,         \n\
+  data    blob NOT NULL,                                                \n\
+  FOREIGN KEY(zone_id) REFERENCES zones(id) ON UPDATE Cascade ON DELETE Cascade); \n\
+                                                                        \n\
+CREATE UNIQUE INDEX rrsets_zone_id_label_type_version                   \n\
+  ON rrsets (zone_id, label, type, version);                            \n\
+";
+
+DbMgr::DbMgr(const std::string& dbFile/* = DEFAULT_CONFIG_PATH "/" "ndns.conf"*/)
+  : m_dbFile(dbFile)
+  , m_conn(0)
+  , m_status(DB_CLOSED)
+{
+  this->open();
+}
+
+
+DbMgr::~DbMgr()
+{
+  if (m_status != DB_CLOSED) {
+    this->close();
+  }
+}
+
+void
+DbMgr::open()
+{
+  if (m_status == DB_CONNECTED)
+    return;
+
+  int res = sqlite3_open_v2(m_dbFile.c_str(), &m_conn,
+                            SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
+#ifdef DISABLE_SQLITE3_FS_LOCKING
+                            "unix-dotfile"
+#else
+                            0
+#endif
+                            );
+
+  if (res != SQLITE_OK) {
+    m_err = "Cannot open the db file: " + m_dbFile;
+    NDNS_LOG_FATAL(m_err);
+    m_status = DB_ERROR;
+  }
+
+  // ignore any errors from DB creation (command will fail for the existing database, which is ok)
+  sqlite3_exec(m_conn, NDNS_SCHEMA.c_str(), 0, 0, 0);
+
+  m_status = DB_CONNECTED;
+}
+
+void
+DbMgr::close()
+{
+  if (m_status == DB_CLOSED)
+    return;
+
+  int ret = sqlite3_close(m_conn);
+  if (ret != SQLITE_OK) {
+    m_err = "Cannot close the db: " + m_dbFile;
+    NDNS_LOG_FATAL(m_err);
+    m_status = DB_ERROR;
+  }
+  m_status = DB_CLOSED;
+}
+
+} // namespace ndns
+} // namespace ndn
diff --git a/src/db/db-mgr.hpp b/src/db/db-mgr.hpp
new file mode 100644
index 0000000..6c5eafd
--- /dev/null
+++ b/src/db/db-mgr.hpp
@@ -0,0 +1,124 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014, Regents of the University of California.
+ *
+ * This file is part of NDNS (Named Data Networking Domain Name Service).
+ * See AUTHORS.md for complete list of NDNS authors and contributors.
+ *
+ * NDNS is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NDNS 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NDNS, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NDNS_DB_DB_MGR_HPP
+#define NDNS_DB_DB_MGR_HPP
+
+#include "config.hpp"
+
+#include <ndn-cxx/common.hpp>
+#include <sqlite3.h>
+
+namespace ndn {
+namespace ndns {
+
+/**
+ * @brief Database Manager, which provides some common DB functionalities
+ */
+class DbMgr : noncopyable
+{
+public:
+  /**
+   * @brief The Database Status
+   */
+  enum DbStatus {
+    DB_CONNECTED,
+    DB_CLOSED,
+    DB_ERROR
+  };
+
+public:
+  /**
+   * @brief constructor
+   */
+  explicit
+  DbMgr(const std::string& dbFile = DEFAULT_CONFIG_PATH "/" "ndns.conf");
+
+  /**
+   * @brief destructor
+   */
+  ~DbMgr();
+
+  /**
+   * @brief connect to the database
+   */
+  void
+  open();
+
+  /**
+   * @brief close the database connection
+   */
+  void
+  close();
+
+  /**
+   * @brief get error message
+   *
+   * only valid when some db-related error happens,
+   * such as db file does not exist, wrong SQL, etc
+   */
+  const std::string&
+  getErr() const
+  {
+    return m_err;
+  }
+
+  /**
+   * @brief get Database connection status
+   */
+  DbStatus
+  getStatus() const
+  {
+    return m_status;
+  }
+
+private:
+  /**
+   * @brief set error message
+   *
+   * only valid when some db-related error happens,
+   * such as db file does not exist, wrong SQL, etc
+   */
+  void
+  setErr(const std::string& err)
+  {
+    this->m_err = err;
+  }
+
+  /**
+   * @brief set Database connection status
+   */
+  void
+  setStatus(DbStatus status)
+  {
+    this->m_status = status;
+  }
+
+private:
+  std::string m_dbFile;
+  sqlite3* m_conn;
+
+  std::string m_err;
+  DbStatus m_status;
+};
+
+} // namespace ndns
+} // namespace ndn
+
+#endif // NDNS_DB_DB_MGR_HPP
diff --git a/src/zone.cpp b/src/zone.cpp
index 6f859a4..3c7fd1a 100644
--- a/src/zone.cpp
+++ b/src/zone.cpp
@@ -18,10 +18,13 @@
  */
 
 #include "zone.hpp"
+#include "logger.hpp"
 
 namespace ndn {
 namespace ndns {
 
+NDNS_LOG_INIT("Zone")
+
 Zone::Zone()
   : m_id(0)
   , m_ttl(3600)
diff --git a/tests/skeleton.cpp b/tests/unit/db/db-mgr.cpp
similarity index 72%
rename from tests/skeleton.cpp
rename to tests/unit/db/db-mgr.cpp
index afa3bde..198a851 100644
--- a/tests/skeleton.cpp
+++ b/tests/unit/db/db-mgr.cpp
@@ -17,17 +17,26 @@
  * NDNS, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-// #include ""
-#include "boost-test.hpp"
+#include "db/db-mgr.hpp"
+#include "../../boost-test.hpp"
 
 namespace ndn {
 namespace ndns {
 namespace tests {
 
-BOOST_AUTO_TEST_SUITE(Skeleton)
+BOOST_AUTO_TEST_SUITE(DbMgr)
 
-BOOST_AUTO_TEST_CASE(TestCase)
+BOOST_AUTO_TEST_CASE(Basic)
 {
+  ndns::DbMgr mgr(BUILDDIR "/tests/unit/db/dbmgr-ndns.db");
+  BOOST_CHECK_EQUAL(mgr.getStatus(), ndns::DbMgr::DB_CONNECTED);
+
+  mgr.close();
+  BOOST_CHECK_EQUAL(mgr.getStatus(), ndns::DbMgr::DB_CLOSED);
+
+  // reopen
+  mgr.open();
+  BOOST_CHECK_EQUAL(mgr.getStatus(), ndns::DbMgr::DB_CONNECTED);
 }
 
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/logger.cpp b/tests/unit/logger.cpp
similarity index 99%
rename from tests/logger.cpp
rename to tests/unit/logger.cpp
index 2352a92..100aa5b 100644
--- a/tests/logger.cpp
+++ b/tests/unit/logger.cpp
@@ -18,7 +18,7 @@
  */
 
 #include "logger.hpp"
-#include "boost-test.hpp"
+#include "../boost-test.hpp"
 
 #include <fstream>
 
diff --git a/tests/unit/zone.cpp b/tests/unit/zone.cpp
index 0a26e76..f533d2e 100644
--- a/tests/unit/zone.cpp
+++ b/tests/unit/zone.cpp
@@ -26,13 +26,12 @@
 namespace ndns {
 namespace tests {
 
+BOOST_AUTO_TEST_SUITE(Zone)
 
-BOOST_AUTO_TEST_SUITE(ZoneTest)
-
-BOOST_AUTO_TEST_CASE(TestCase)
+BOOST_AUTO_TEST_CASE(Basic)
 {
   Name zoneName("/net/ndnsim");
-  Zone zone1;
+  ndns::Zone zone1;
   zone1.setName(zoneName);
   zone1.setId(2);
   zone1.setTtl(time::seconds(4000));
@@ -41,11 +40,11 @@
   BOOST_CHECK_EQUAL(zone1.getName(), zoneName);
   BOOST_CHECK_EQUAL(zone1.getTtl(), time::seconds(4000));
 
-  Zone zone2(zoneName);
+  ndns::Zone zone2(zoneName);
   BOOST_CHECK_EQUAL(zone1, zone2);
   BOOST_CHECK_EQUAL(zone2.getName(), zone1.getName());
 
-  BOOST_CHECK_NE(zone1, Zone("/net/ndnsim2"));
+  BOOST_CHECK_NE(zone1, ndns::Zone("/net/ndnsim2"));
 }
 
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/wscript b/tests/wscript
index 383ae8c..0407962 100644
--- a/tests/wscript
+++ b/tests/wscript
@@ -1,6 +1,6 @@
 # -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
-from waflib import Utils
+from waflib import Utils, Context
 
 top = '..'
 
@@ -12,4 +12,5 @@
             source=bld.path.ant_glob(['**/*.cpp']),
             use='ndns-objects',
             install_path=None,
+            defines="BUILDDIR=\"%s\"" % Context.out_dir,
           )
diff --git a/wscript b/wscript
index 5886fc1..9c5bd1f 100644
--- a/wscript
+++ b/wscript
@@ -43,6 +43,9 @@
     if not conf.options.with_sqlite_locking:
         conf.define('DISABLE_SQLITE3_FS_LOCKING', 1)
 
+    conf.define("DEFAULT_CONFIG_PATH", "%s/ndn" % conf.env['SYSCONFDIR'])
+    conf.define("DEFAULT_DATABASE_PATH", "%s/ndn/ndns" % conf.env['LOCALSTATEDIR'])
+
     conf.write_config_header('src/config.hpp')
 
 def build (bld):