add_subdirectory(nmlib)
add_subdirectory(test)
add_subdirectory(agent)
-
+add_subdirectory(nmdaemon)
--- /dev/null
+get_filename_component(ProjectId ${CMAKE_CURRENT_SOURCE_DIR} NAME)
+string(REPLACE " " "_" ProjectId ${ProjectId})
+project(${ProjectId} C CXX)
+
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+
+#set(CMAKE_THREAD_PREFER_PTHREADS ON)
+
+file(GLOB NMDAEMON_SOURCES *.cpp)
+add_executable(${PROJECT_NAME} ${NMDAEMON_SOURCES})
+
+target_link_libraries (${PROJECT_NAME} pthread)
+
+install(TARGETS ${PROJECT_NAME} DESTINATION ${TESTS_DIR})
+install(FILES nmdaemon.manifest DESTINATION ${MANIFESTDIR})
+
+message(STATUS "Configuring: " ${ProjectId})
+message(STATUS "CMAKE_CXX_FLAGS: " ${CMAKE_CXX_FLAGS})
--- /dev/null
+#include <stdlib.h>\r
+#include <stdio.h>\r
+#include <signal.h>\r
+#include <unistd.h>\r
+#include <execinfo.h>\r
+#include <wait.h>\r
+#include <string.h>\r
+#include <errno.h>\r
+\r
+#include "main_def.h"\r
+\r
+#include "daemon.h"\r
+\r
+#include "utils.h"\r
+\r
+using namespace NMD;\r
+\r
+/*******************************************************/\r
+/*******************************************************/\r
+int daemon::run()\r
+{\r
+ sigset_t sigset;\r
+ int signo;\r
+\r
+ write_log("[DAEMON] started\n");\r
+\r
+ m_sigact.sa_flags = SA_SIGINFO;\r
+ m_sigact.sa_sigaction = daemon::err_signal_handler;\r
+ sigemptyset(&m_sigact.sa_mask);\r
+ sigaction(SIGFPE, &m_sigact, 0);\r
+ sigaction(SIGILL, &m_sigact, 0);\r
+ sigaction(SIGSEGV, &m_sigact, 0);\r
+ sigaction(SIGBUS, &m_sigact, 0);\r
+\r
+ sigemptyset(&sigset);\r
+ sigaddset(&sigset, SIGQUIT);\r
+ sigaddset(&sigset, SIGINT);\r
+ sigaddset(&sigset, SIGTERM);\r
+ sigaddset(&sigset, SIGUSR1);\r
+ sigprocmask(SIG_BLOCK, &sigset, NULL);\r
+\r
+ if(threads_init())\r
+ {\r
+ while(true)\r
+ {\r
+ sigwait(&sigset, &signo);\r
+ write_log("[DAEMON] signal %s\n", strsignal(signo));\r
+ break;\r
+ }\r
+\r
+ threads_done();\r
+ }\r
+ else\r
+ {\r
+ write_log("[DAEMON] can't create threads\n");\r
+ }\r
+\r
+ write_log("[DAEMON] stopped\n");\r
+\r
+ return NMD_NEED_TERMINATE;\r
+}\r
+\r
+/*******************************************************/\r
+/*******************************************************/\r
+bool daemon::threads_init()\r
+{\r
+ return m_main_thread.start();\r
+}\r
+void daemon::threads_done()\r
+{\r
+ m_main_thread.stop();\r
+}\r
+\r
+/*******************************************************/\r
+/*******************************************************/\r
+/*static*/ void daemon::err_signal_handler(int _signo, siginfo_t* _signal_info, void* _ptr)\r
+{\r
+#if 0\r
+ void* error_addr;\r
+ void* trace[16];\r
+ int trace_size;\r
+ char** messages;\r
+\r
+ write_log("[DAEMON] signal: %s, addr: 0x%0.16X\n", strsignal(_signo), _signal_info->si_addr);\r
+\r
+#if(__WORDSIZE == 64)\r
+ error_addr = (void*)((ucontext_t*)_ptr)->uc_mcontext.gregs[REG_RIP];\r
+#else\r
+ error_addr = (void*)((ucontext_t*)_ptr)->uc_mcontext.gregs[REG_EIP];\r
+#endif\r
+\r
+ trace_size = backtrace(trace, 16);\r
+ trace[1] = error_addr;\r
+\r
+ messages = backtrace_symbols(trace, trace_size);\r
+\r
+ if(messages)\r
+ {\r
+ write_log("=== backtrace begin ===\n");\r
+\r
+ for(int i = 1; i < trace_size; i++)\r
+ write_log("%s\n", messages[i]);\r
+\r
+ write_log("=== backtrace end ===\n");\r
+\r
+ free(messages);\r
+ }\r
+#endif\r
+\r
+ daemon* pd = reinterpret_cast<daemon*>(_signal_info->si_value.sival_ptr);\r
+ pd->threads_done();\r
+\r
+ write_log("[DAEMON] stopped\n");\r
+\r
+ exit(NMD_NEED_RESTART);\r
+}\r
--- /dev/null
+#ifndef __DAEMON_H__\r
+#define __DAEMON_H__\r
+\r
+#include "main_thread.h"\r
+\r
+namespace NMD\r
+{\r
+\r
+/*******************************************************/\r
+/*******************************************************/\r
+class daemon final\r
+{\r
+public:\r
+ daemon() = default;\r
+ daemon(const daemon&) = delete;\r
+\r
+ ~daemon() = default;\r
+\r
+ daemon& operator=(const daemon&) = delete;\r
+\r
+ int run();\r
+\r
+private:\r
+ bool threads_init();\r
+ void threads_done();\r
+\r
+ static void err_signal_handler(int _signo, siginfo_t* _signal_info, void* _ptr);\r
+\r
+ struct sigaction m_sigact;\r
+\r
+ main_thread m_main_thread;\r
+};\r
+\r
+}\r
+\r
+#endif /* __DAEMON_H__ */\r
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <string.h>
+#include <unistd.h>
+#include <execinfo.h>
+#include <wait.h>
+#include <errno.h>
+
+#include "main_def.h"
+
+#include "monitor.h"
+
+using namespace NMD;
+
+/*******************************************************/
+/*******************************************************/
+int main(int /*argc*/, char** /*argv*/)
+{
+ int res = 0;
+ switch(fork())
+ {
+ case -1:
+ {
+ printf("Error: can't start nmd: %s\n", strerror(errno));
+ res = -1;
+ }
+ break;
+ case 0:
+ {
+ umask(0);
+ setsid();
+ chdir("/");
+
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+
+ res = monitor::run();
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return res;
+}
--- /dev/null
+#ifndef __MAIN_DEF_H__
+#define __MAIN_DEF_H__
+
+/*******************************************************/
+/*******************************************************/
+#define PID_FILE_NAME "/var/run/nmd.pid"
+#define LOG_FILE_NAME "/var/log/nmd.log"
+
+/*******************************************************/
+/*******************************************************/
+#define NMD_NEED_RESTART 1
+#define NMD_NEED_TERMINATE 2
+
+/*******************************************************/
+/*******************************************************/
+#define MAIN_THREAD_SLEEP_US 1000
+
+/*******************************************************/
+/*******************************************************/
+#define MAIN_THREAD_SOCKET_HOST "localhost"
+#define MAIN_THREAD_SOCKET_PORT 12345
+#define MAIN_THREAD_SOCKET_TIMEOUT_MS 100
+
+#endif /* __MAIN_DEF_H__ */
--- /dev/null
+#include <stdlib.h>\r
+#include <unistd.h>\r
+\r
+#include "main_def.h"\r
+\r
+#include "main_thread.h"\r
+\r
+#include "socket_base.h"\r
+#include "utils.h"\r
+\r
+using namespace NMD;\r
+\r
+/*******************************************************/\r
+/*******************************************************/\r
+main_thread::main_thread() : thread_base()\r
+{\r
+}\r
+/*virtual*/ main_thread::~main_thread()\r
+{\r
+}\r
+\r
+/*******************************************************/\r
+/*******************************************************/\r
+/*virtual*/ void main_thread::routine()\r
+{\r
+ static char bbb[1024];\r
+\r
+ write_log("[MAIN_THREADS] started\n");\r
+\r
+ socket_base sock(true);\r
+ if(sock.bind(MAIN_THREAD_SOCKET_HOST, MAIN_THREAD_SOCKET_PORT) && sock.listen())\r
+ {\r
+ while(m_running)\r
+ {\r
+ int fd = sock.accept(MAIN_THREAD_SOCKET_TIMEOUT_MS);\r
+ if(fd != -1)\r
+ {\r
+ int s = socket_base::read(fd, bbb, 1024);\r
+\r
+ if(s <= 0)\r
+ {\r
+ write_log("[MAIN_THREADS] %d bytes was received\n", s);\r
+ }\r
+ else\r
+ {\r
+ bbb[s] = 0;\r
+ write_log("[MAIN_THREADS] %d bytes was received - %s\n", s, bbb);\r
+ }\r
+\r
+ if(s > 0)\r
+ socket_base::write(fd, bbb, s);\r
+\r
+ socket_base::close(fd);\r
+ }\r
+ }\r
+ }\r
+\r
+ write_log("[MAIN_THREADS] stopped\n");\r
+}\r
--- /dev/null
+#ifndef __MAIN_THREAD_H__\r
+#define __MAIN_THREAD_H__\r
+\r
+#include "thread_base.h"\r
+\r
+namespace NMD\r
+{\r
+\r
+/*******************************************************/\r
+/*******************************************************/\r
+class main_thread : public thread_base\r
+{\r
+public:\r
+ main_thread();\r
+\r
+ virtual ~main_thread();\r
+\r
+ virtual void routine();\r
+\r
+private:\r
+};\r
+\r
+}\r
+\r
+#endif /* __MAIN_THREAD_H__ */\r
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <unistd.h>
+#include <execinfo.h>
+#include <wait.h>
+#include <string.h>
+#include <errno.h>
+
+#include "main_def.h"
+
+#include "monitor.h"
+
+#include "daemon.h"
+#include "utils.h"
+
+using namespace NMD;
+
+/*******************************************************/
+/*******************************************************/
+/*static*/ int monitor::run()
+{
+ bool start = true;
+ int pid = -1;
+ sigset_t sigset;
+ siginfo_t siginfo;
+ int status = 0;
+
+ write_log("[MONITOR] started\n");
+
+ sigemptyset(&sigset);
+ sigaddset(&sigset, SIGQUIT);
+ sigaddset(&sigset, SIGINT);
+ sigaddset(&sigset, SIGTERM);
+ sigaddset(&sigset, SIGCHLD);
+ sigaddset(&sigset, SIGUSR1);
+ sigprocmask(SIG_BLOCK, &sigset, NULL);
+
+ save_pid();
+
+ while(true)
+ {
+ if(start)
+ {
+ start = false;
+ pid = fork();
+ }
+
+ if(pid == -1)
+ {
+ start = true;
+ write_log("[MONITOR] fork() failed (%s)\n", strerror(errno));
+ }
+ else if(!pid)
+ {
+ daemon d;
+ exit(d.run());
+ }
+ else
+ {
+ sigwaitinfo(&sigset, &siginfo);
+
+ if(siginfo.si_signo == SIGCHLD)
+ {
+ wait(&status);
+ status = WEXITSTATUS(status);
+
+ if(status == NMD_NEED_RESTART)
+ {
+ start = true;
+ write_log("[MONITOR] child will be restarted\n");
+ }
+ else if(status == NMD_NEED_TERMINATE)
+ {
+ write_log("[MONITOR] child stopped\n");
+ break;
+ }
+ }
+ else if(siginfo.si_signo == SIGUSR1)
+ {
+ write_log("[MONITOR] signal %s\n", strsignal(siginfo.si_signo));
+ kill(pid, SIGUSR1);
+ }
+ else
+ {
+ status = 0;
+ write_log("[MONITOR] signal %s\n", strsignal(siginfo.si_signo));
+ kill(pid, SIGTERM);
+ break;
+ }
+ }
+ }
+
+ unlink(PID_FILE_NAME);
+
+ write_log("[MONITOR] stopped\n");
+
+ return status;
+}
+
+/*******************************************************/
+/*******************************************************/
+/*static*/ void monitor::save_pid()
+{
+ FILE* file = fopen(PID_FILE_NAME, "w+");
+ if(file)
+ {
+ fprintf(file, "%u", getpid());
+ fclose(file);
+ }
+}
--- /dev/null
+#ifndef __MONITOR_H__
+#define __MONITOR_H__
+
+namespace NMD
+{
+
+/*******************************************************/
+/*******************************************************/
+class monitor final
+{
+public:
+ monitor() = default;
+ monitor(const monitor&) = delete;
+
+ ~monitor() = default;
+
+ monitor& operator=(const monitor&) = delete;
+
+ static int run();
+
+private:
+ static void save_pid();
+};
+
+}
+
+#endif /* __MONITOR_H__ */
--- /dev/null
+<manifest>
+ <define>
+ <domain name='nm' policy='shared' />
+ <request>
+ <smack request="device::app_logging" type="w"/>
+ <smack request="system::use_internet" type="rw"/>
+ <smack request="system::run" type="rwxat"/>
+ </request>
+ <permit>
+ <smack permit="system::use_internet" type="rw"/>
+ </permit>
+ </define>
+</manifest>
--- /dev/null
+#include <stdlib.h>\r
+#include <unistd.h>\r
+#include <sys/types.h>\r
+#include <sys/socket.h>\r
+#include <netinet/in.h>\r
+#include <netinet/ip.h>\r
+#include <netdb.h>\r
+#include <sys/un.h>\r
+#include <sys/select.h>\r
+#include <fcntl.h>\r
+#include <sys/time.h>\r
+\r
+#include "socket_base.h"\r
+\r
+using namespace NMD;\r
+\r
+/*******************************************************/\r
+/*******************************************************/\r
+socket_base::socket_base(bool _non_block /*= false*/) : m_socket_fd(0)\r
+{\r
+ m_socket_fd = socket(AF_INET, SOCK_STREAM, 0);\r
+ if(_non_block)\r
+ fcntl(m_socket_fd, F_SETFL, O_NONBLOCK);\r
+}\r
+socket_base::socket_base(const socket_base &_obj) : m_socket_fd(_obj.m_socket_fd)\r
+{\r
+}\r
+\r
+/*******************************************************/\r
+/*******************************************************/\r
+/*virtual*/ socket_base::~socket_base()\r
+{\r
+ close(m_socket_fd);\r
+}\r
+\r
+/*******************************************************/\r
+/*******************************************************/\r
+/*virtual*/ socket_base& socket_base::operator=(const socket_base& _obj)\r
+{\r
+ if(this != &_obj)\r
+ {\r
+ m_socket_fd = _obj.m_socket_fd;\r
+ }\r
+ return *this;\r
+}\r
+\r
+/*******************************************************/\r
+/*******************************************************/\r
+/*virtual*/ bool socket_base::bind(const char* _host, int _port)\r
+{\r
+ struct sockaddr_in info;\r
+ if(!socket_info(&info, _host, _port))\r
+ return false;\r
+ return (::bind(m_socket_fd, (struct sockaddr*)&info, sizeof(info)) < 0) ? false : true;\r
+}\r
+/*virtual*/ bool socket_base::listen()\r
+{\r
+ return (::listen(m_socket_fd, m_connections) < 0) ? false : true;\r
+}\r
+/*virtual*/ int socket_base::accept(int _wait_timeout_ms)\r
+{\r
+ fd_set rset;\r
+ struct timeval timeout;\r
+ int res = -1;\r
+\r
+ FD_ZERO(&rset);\r
+ FD_SET(m_socket_fd, &rset);\r
+\r
+ timeout.tv_sec = 0;\r
+ timeout.tv_usec = _wait_timeout_ms * 1000 + 1;\r
+\r
+ if(::select(m_socket_fd + 1, &rset, NULL, NULL, &timeout) > 0)\r
+ {\r
+ if( FD_ISSET(m_socket_fd, &rset) )\r
+ {\r
+ struct sockaddr_in info;\r
+ socklen_t ilen = sizeof(info);\r
+ res = ::accept(m_socket_fd, (struct sockaddr*)&info, &ilen);\r
+ }\r
+ }\r
+\r
+ return res;\r
+}\r
+\r
+/*******************************************************/\r
+/*******************************************************/\r
+/*virtual*/ bool socket_base::connect(const char* _host, int _port)\r
+{\r
+ struct sockaddr_in info;\r
+ if(!socket_info(&info, _host, _port))\r
+ return false;\r
+ return (::connect(m_socket_fd, (struct sockaddr*)&info, sizeof(info)) < 0) ? false : true;\r
+}\r
+\r
+/*******************************************************/\r
+/*******************************************************/\r
+/*virtual*/ bool socket_base::write(const void* _data, int _size)\r
+{\r
+ return write(m_socket_fd, _data, _size);\r
+}\r
+/*virtual*/ int socket_base::read(void* _buff, int _size)\r
+{\r
+ return read(m_socket_fd, _buff, _size);\r
+}\r
+\r
+/*******************************************************/\r
+/*******************************************************/\r
+/*static*/ bool socket_base::write(int _socket, const void* _data, int _size)\r
+{\r
+ return (::write(_socket, _data, _size) == _size);\r
+}\r
+/*static*/ int socket_base::read(int _socket, void* _buff, int _size)\r
+{\r
+ return (int)::read(_socket, _buff, _size);\r
+}\r
+\r
+/*******************************************************/\r
+/*******************************************************/\r
+/*static*/ void socket_base::close(int _socket)\r
+{\r
+ ::close(_socket);\r
+}\r
+\r
+/*******************************************************/\r
+/*******************************************************/\r
+/*static*/ bool socket_base::socket_info(struct sockaddr_in* _info, const char* _host, int _port)\r
+{\r
+ struct in_addr addr;\r
+ int port;\r
+\r
+ {\r
+ struct hostent* lhi = (struct hostent*)gethostbyname(_host);\r
+ if( (lhi == NULL) || (lhi->h_length == 0) )\r
+ return false;\r
+\r
+ addr.s_addr = *( (int*)(lhi->h_addr_list[0]) );\r
+ }\r
+\r
+ port = (int)htons(_port);\r
+\r
+ memset(_info, 0, sizeof(struct sockaddr_in));\r
+ _info->sin_family = AF_INET;\r
+ _info->sin_addr = addr;\r
+ _info->sin_port = port;\r
+\r
+ return true;\r
+}\r
--- /dev/null
+#ifndef __SOCKET_BASE_H__\r
+#define __SOCKET_BASE_H__\r
+\r
+namespace NMD\r
+{\r
+\r
+/*******************************************************/\r
+/*******************************************************/\r
+class socket_base\r
+{\r
+public:\r
+ socket_base(bool _non_block = false);\r
+ socket_base(const socket_base& _obj);\r
+\r
+ virtual ~socket_base();\r
+\r
+ virtual socket_base& operator=(const socket_base& _obj);\r
+\r
+ virtual bool bind(const char* _host, int _port);\r
+ virtual bool listen();\r
+ virtual int accept(int _wait_timeout_ms);\r
+\r
+ virtual bool connect(const char* _host, int _port);\r
+\r
+ virtual bool write(const void* _data, int _size);\r
+ virtual int read(void* _buff, int _size);\r
+\r
+ static bool write(int _socket, const void* _data, int _size);\r
+ static int read(int _socket, void* _buff, int _size);\r
+\r
+ static void close(int _socket);\r
+\r
+ static bool socket_info(struct sockaddr_in* _info, const char* _host, int _port);\r
+\r
+protected:\r
+ int m_socket_fd;\r
+\r
+private:\r
+ static const int m_connections = 5;\r
+};\r
+\r
+}\r
+\r
+#endif /* __SOCKET_BASE_H__ */\r
--- /dev/null
+#include <stdlib.h>\r
+\r
+#include "thread_base.h"\r
+\r
+using namespace NMD;\r
+\r
+/*******************************************************/\r
+/*******************************************************/\r
+thread_base::thread_base() : m_id(0), m_started(false), m_running(false)\r
+{\r
+}\r
+/*virtual*/ thread_base::~thread_base()\r
+{\r
+}\r
+\r
+/*******************************************************/\r
+/*******************************************************/\r
+/*virtual*/bool thread_base::start()\r
+{\r
+ m_running = true;\r
+\r
+ if(pthread_create(&m_id, 0, [](void* _ptr){static_cast<thread_base*>(_ptr)->routine();return (void*)nullptr;}, this) != 0)\r
+ return false;\r
+\r
+ return (m_started = true);\r
+}\r
+/*virtual*/void thread_base::stop()\r
+{\r
+ m_running = false;\r
+\r
+ if(m_started)\r
+ pthread_join(m_id, NULL);\r
+}\r
+\r
+/*******************************************************/\r
+/*******************************************************/\r
+/*virtual*/ void thread_base::routine()\r
+{\r
+ while(m_running)\r
+ ;\r
+}\r
--- /dev/null
+#ifndef __THREAD_BASE_H__\r
+#define __THREAD_BASE_H__\r
+\r
+#include <pthread.h>\r
+\r
+namespace NMD\r
+{\r
+\r
+/*******************************************************/\r
+/*******************************************************/\r
+class thread_base\r
+{\r
+public:\r
+ thread_base();\r
+ thread_base(const thread_base&) = delete;\r
+\r
+ virtual ~thread_base();\r
+\r
+ thread_base& operator=(const thread_base&) = delete;\r
+\r
+ virtual bool start();\r
+ virtual void stop();\r
+\r
+ virtual void routine();\r
+\r
+private:\r
+ pthread_t m_id;\r
+ bool m_started;\r
+\r
+protected:\r
+ volatile bool m_running;\r
+};\r
+\r
+}\r
+\r
+#endif /* __THREAD_BASE_H__ */\r
--- /dev/null
+#include <stdlib.h>\r
+#include <stdarg.h>\r
+#include <stdio.h>\r
+\r
+#include "main_def.h"\r
+\r
+#include "utils.h"\r
+\r
+namespace NMD\r
+{\r
+\r
+/*******************************************************/\r
+/*******************************************************/\r
+static bool wl(const char* _msg, va_list _vl);\r
+\r
+/*******************************************************/\r
+/*******************************************************/\r
+bool write_log(const char* _msg, ...)\r
+{\r
+ bool res = false;\r
+ va_list vl;\r
+\r
+ va_start(vl, _msg);\r
+ res = wl(_msg, vl);\r
+ va_end(vl);\r
+\r
+ return res;\r
+}\r
+\r
+/*******************************************************/\r
+/*******************************************************/\r
+static bool wl(const char* _msg, va_list _vl)\r
+{\r
+ bool res = false;\r
+\r
+ FILE* file = fopen(LOG_FILE_NAME, "a+");\r
+ if(file)\r
+ {\r
+ res = (bool)vfprintf(file, _msg, _vl);\r
+ fclose(file);\r
+ }\r
+\r
+ return res;\r
+}\r
+\r
+}\r
--- /dev/null
+#ifndef __UTILS_H__\r
+#define __UTILS_H__\r
+\r
+namespace NMD\r
+{\r
+\r
+/*******************************************************/\r
+/*******************************************************/\r
+bool write_log(const char* _msg, ...);\r
+\r
+}\r
+\r
+#endif /* __UTILS_H__ */\r
%files agent
%attr(0755,root,root) %{_tests_dir}/agent
+##############################################
+# nmdaemon
+##############################################
+
+%package nmdaemon
+Summary: nmdaemon
+%description nmdaemon
+nmdaemon
+%files nmdaemon
+%manifest %{_manifestdir}/nmdaemon.manifest
+%attr(0755,root,root) %{_tests_dir}/nmdaemon