daemon: basic systemd integration

Notify systemd when NFD is ready/reloading/terminating

Change-Id: I833b48fbcaf6ecc2c4bb8a1da67d4eb9a050c116
Refs: #2815
diff --git a/.jenkins.d/00-deps.sh b/.jenkins.d/00-deps.sh
index 6bf3ddd..a9b3a34 100755
--- a/.jenkins.d/00-deps.sh
+++ b/.jenkins.d/00-deps.sh
@@ -25,7 +25,7 @@
 if has Ubuntu $NODE_LABELS; then
     sudo apt-get -qq update
     sudo apt-get -qy install build-essential pkg-config libboost-all-dev \
-                             libsqlite3-dev libssl-dev libpcap-dev
+                             libsqlite3-dev libssl-dev libpcap-dev libsystemd-dev
 
     if [[ $JOB_NAME == *"code-coverage" ]]; then
         sudo apt-get -qy install lcov libgd-perl python-setuptools
@@ -34,9 +34,10 @@
 fi
 
 if has CentOS $NODE_LABELS; then
-    sudo yum -y install yum-utils pkgconfig libpcap-devel \
+    sudo yum -y install yum-utils pkgconfig \
                         openssl-devel libtranslit-icu \
                         python-devel sqlite-devel \
+                        libpcap-devel systemd-devel \
                         devtoolset-7-libasan-devel \
                         devtoolset-7-liblsan-devel
     sudo yum -y groupinstall 'Development Tools'
diff --git a/contrib/systemd/README.md b/contrib/systemd/README.md
index 7c722ab..d129b5e 100644
--- a/contrib/systemd/README.md
+++ b/contrib/systemd/README.md
@@ -1,8 +1,8 @@
 Starting NFD on Linux with systemd
 ==================================
 
-Newer versions of Ubuntu (starting with 15.04) and some other Linux distributions, including Debian
-use systemd to start system daemons, monitor their health, and restart them when they die.
+Modern versions of Ubuntu (starting with 15.04) and some other Linux distributions, including Debian
+and Fedora, use systemd to start system daemons, monitor their health, and restart them when they die.
 
 Initial setup
 -------------
@@ -127,4 +127,4 @@
 To permanently stop the `nfd` daemon and disable it from being automatically started on reboot,
 disable the service:
 
-    sudo systemctl disable nfd
\ No newline at end of file
+    sudo systemctl disable nfd
diff --git a/contrib/systemd/nfd.service b/contrib/systemd/nfd.service
index 063e4a7..57661fd 100644
--- a/contrib/systemd/nfd.service
+++ b/contrib/systemd/nfd.service
@@ -30,9 +30,10 @@
 After=network-online.target
 
 [Service]
+Type=notify
 Environment=HOME=/usr/local/var/lib/ndn/nfd
 ExecStart=/usr/local/bin/nfd --config /usr/local/etc/ndn/nfd.conf
-ExecStartPost=/bin/sh -ec 'sleep 2; if [ -f /usr/local/etc/ndn/nfd-init.sh ]; then . /usr/local/etc/ndn/nfd-init.sh; fi'
+ExecStartPost=/bin/sh -ec 'if [ -f /usr/local/etc/ndn/nfd-init.sh ]; then . /usr/local/etc/ndn/nfd-init.sh; fi'
 ExecReload=/bin/kill -HUP $MAINPID
 Restart=on-failure
 RestartPreventExitStatus=2 4
diff --git a/daemon/main.cpp b/daemon/main.cpp
index f6d482d..5999ea1 100644
--- a/daemon/main.cpp
+++ b/daemon/main.cpp
@@ -54,6 +54,9 @@
 #ifdef HAVE_LIBPCAP
 #include <pcap/pcap.h>
 #endif
+#ifdef HAVE_SYSTEMD
+#include <systemd/sd-daemon.h>
+#endif
 #ifdef HAVE_WEBSOCKET
 #include <websocketpp/version.hpp>
 #endif
@@ -151,6 +154,7 @@
     }
 
     try {
+      systemdNotify("READY=1");
       mainIo->run();
     }
     catch (const std::exception& e) {
@@ -175,6 +179,15 @@
     return retval;
   }
 
+  static void
+  systemdNotify(const char* state)
+  {
+#ifdef HAVE_SYSTEMD
+    sd_notify(0, state);
+#endif
+  }
+
+private:
   void
   terminate(const boost::system::error_code& error, int signalNo)
   {
@@ -182,6 +195,8 @@
       return;
 
     NFD_LOG_INFO("Caught signal '" << ::strsignal(signalNo) << "', exiting...");
+
+    systemdNotify("STOPPING=1");
     getGlobalIoService().stop();
   }
 
@@ -192,7 +207,10 @@
       return;
 
     NFD_LOG_INFO("Caught signal '" << ::strsignal(signalNo) << "', reloading...");
+
+    systemdNotify("RELOADING=1");
     m_nfd.reloadConfigFile();
+    systemdNotify("READY=1");
 
     m_reloadSignalSet.async_wait(bind(&NfdRunner::reload, this, _1, _2));
   }
diff --git a/wscript b/wscript
index c5f8b33..6773293 100644
--- a/wscript
+++ b/wscript
@@ -41,15 +41,16 @@
              tooldir=['.waf-tools'])
 
     nfdopt = opt.add_option_group('NFD Options')
-    opt.addUnixOptions(nfdopt)
-    opt.addWebsocketOptions(nfdopt)
 
+    opt.addUnixOptions(nfdopt)
+    opt.addDependencyOptions(nfdopt, 'libresolv')
+    opt.addDependencyOptions(nfdopt, 'librt')
     opt.addDependencyOptions(nfdopt, 'libpcap')
     nfdopt.add_option('--without-libpcap', action='store_true', default=False,
                       help='Disable libpcap (Ethernet face support will be disabled)')
-
-    opt.addDependencyOptions(nfdopt, 'librt')
-    opt.addDependencyOptions(nfdopt, 'libresolv')
+    nfdopt.add_option('--without-systemd', action='store_true', default=False,
+                      help='Disable systemd integration')
+    opt.addWebsocketOptions(nfdopt)
 
     nfdopt.add_option('--with-tests', action='store_true', default=False,
                       help='Build unit tests')
@@ -87,6 +88,13 @@
                'pch', 'boost', 'dependency-checker', 'websocket',
                'doxygen', 'sphinx_build'])
 
+    if conf.options.with_tests:
+        conf.env.WITH_TESTS = True
+        conf.define('WITH_TESTS', 1)
+    if conf.options.with_other_tests:
+        conf.env.WITH_OTHER_TESTS = True
+        conf.define('WITH_OTHER_TESTS', 1)
+
     conf.find_program('bash', var='BASH')
 
     if 'PKG_CONFIG_PATH' not in os.environ:
@@ -94,6 +102,9 @@
     conf.check_cfg(package='libndn-cxx', args=['--cflags', '--libs'],
                    uselib_store='NDN_CXX', mandatory=True)
 
+    conf.check_cfg(package='libsystemd', args=['--cflags', '--libs'],
+                   uselib_store='SYSTEMD', mandatory=False)
+
     conf.checkDependency(name='librt', lib='rt', mandatory=False)
     conf.checkDependency(name='libresolv', lib='resolv', mandatory=False)
 
@@ -103,14 +114,6 @@
 
     conf.check_cxx(header_name='valgrind/valgrind.h', define_name='HAVE_VALGRIND', mandatory=False)
 
-    if conf.options.with_tests:
-        conf.env.WITH_TESTS = True
-        conf.define('WITH_TESTS', 1)
-
-    if conf.options.with_other_tests:
-        conf.env.WITH_OTHER_TESTS = True
-        conf.define('WITH_OTHER_TESTS', 1)
-
     boost_libs = 'system chrono program_options thread log log_setup'
     if conf.options.with_tests or conf.options.with_other_tests:
         boost_libs += ' unit_test_framework'
@@ -122,13 +125,14 @@
                    ' (https://redmine.named-data.net/projects/nfd/wiki/Boost_FAQ)')
 
     conf.load('unix-socket')
-    conf.checkWebsocket(mandatory=True)
 
     if not conf.options.without_libpcap:
         conf.checkDependency(name='libpcap', lib='pcap', mandatory=True,
                              errmsg='not found, but required for Ethernet face support. '
                                     'Specify --without-libpcap to disable Ethernet face support.')
 
+    conf.checkWebsocket(mandatory=True)
+
     conf.check_compiler_flags()
 
     # Loading "late" to prevent tests from being compiled with profiling flags
@@ -201,7 +205,7 @@
     bld.program(name='nfd',
                 target='bin/nfd',
                 source='daemon/main.cpp',
-                use='daemon-objects rib-objects')
+                use='daemon-objects rib-objects SYSTEMD')
 
     bld.recurse('tools')
     bld.recurse('tests')