/** * \param[in] argc argument count * \param[in] argv argument array * \return 0 on success, 1 on error * * \attention In daemon mode, it finishes immediately. */ int main(int argc, char** argv) { openlog(INCRON_DAEMON_NAME, INCRON_LOG_OPTS, INCRON_LOG_FACIL); syslog(LOG_NOTICE, "starting service (version %s, built on %s %s)", INCRON_VERSION, __DATE__, __TIME__); try { Inotify in; in.SetNonBlock(true); EventDispatcher ed(&in); try { load_tables(&in, &ed); } catch (InotifyException e) { int err = e.GetErrorNumber(); syslog(LOG_CRIT, "%s: (%i) %s", e.GetMessage().c_str(), err, strerror(err)); syslog(LOG_NOTICE, "stopping service"); closelog(); return 1; } signal(SIGTERM, on_signal); signal(SIGINT, on_signal); signal(SIGCHLD, on_signal); if (DAEMON) daemon(0, 0); uint32_t wm = IN_CLOSE_WRITE | IN_DELETE | IN_MOVE | IN_DELETE_SELF | IN_UNMOUNT; InotifyWatch watch(INCRON_TABLE_BASE, wm); in.Add(watch); syslog(LOG_NOTICE, "ready to process filesystem events"); InotifyEvent e; struct pollfd pfd; pfd.fd = in.GetDescriptor(); pfd.events = (short) POLLIN; pfd.revents = (short) 0; while (!g_fFinish) { int res = poll(&pfd, 1, -1); if (res > 0) { in.WaitForEvents(true); } else if (res < 0) { if (errno != EINTR) throw InotifyException("polling failed", errno, NULL); } UserTable::FinishDone(); while (in.GetEvent(e)) { if (e.GetWatch() == &watch) { if (e.IsType(IN_DELETE_SELF) || e.IsType(IN_UNMOUNT)) { syslog(LOG_CRIT, "base directory destroyed, exitting"); g_fFinish = true; } else if (!e.GetName().empty()) { SUT_MAP::iterator it = g_ut.find(e.GetName()); if (it != g_ut.end()) { UserTable* pUt = (*it).second; if (e.IsType(IN_CLOSE_WRITE) || e.IsType(IN_MOVED_TO)) { syslog(LOG_INFO, "table for user %s changed, reloading", e.GetName().c_str()); pUt->Dispose(); pUt->Load(); } else if (e.IsType(IN_MOVED_FROM) || e.IsType(IN_DELETE)) { syslog(LOG_INFO, "table for user %s destroyed, removing", e.GetName().c_str()); delete pUt; g_ut.erase(it); } } else if (e.IsType(IN_CLOSE_WRITE) || e.IsType(IN_MOVED_TO)) { if (check_user(e.GetName().c_str())) { syslog(LOG_INFO, "table for user %s created, loading", e.GetName().c_str()); UserTable* pUt = new UserTable(&in, &ed, e.GetName()); g_ut.insert(SUT_MAP::value_type(e.GetName(), pUt)); pUt->Load(); } } } } else { ed.DispatchEvent(e); } } } } catch (InotifyException e) { int err = e.GetErrorNumber(); syslog(LOG_CRIT, "*** unhandled exception occurred ***"); syslog(LOG_CRIT, " %s", e.GetMessage().c_str()); syslog(LOG_CRIT, " error: (%i) %s", err, strerror(err)); } syslog(LOG_NOTICE, "stopping service"); closelog(); return 0; }
/** * \param[in] argc argument count * \param[in] argv argument array * \return 0 on success, 1 on error * * \attention In daemon mode, it finishes immediately. */ int main(int argc, char** argv) { AppArgs::Init(); if (!( AppArgs::AddOption("about", '?', AAT_NO_VALUE, false) && AppArgs::AddOption("help", 'h', AAT_NO_VALUE, false) && AppArgs::AddOption("foreground", 'n', AAT_NO_VALUE, false) && AppArgs::AddOption("kill", 'k', AAT_NO_VALUE, false) && AppArgs::AddOption("config", 'f', AAT_MANDATORY_VALUE, false) && AppArgs::AddOption("version", 'V', AAT_NO_VALUE, false))) { fprintf(stderr, "error while initializing application"); return 1; } AppArgs::Parse(argc, argv); if (AppArgs::ExistsOption("help")) { fprintf(stderr, "%s\n", INCROND_HELP); return 0; } if (AppArgs::ExistsOption("about")) { fprintf(stderr, "%s\n", INCROND_DESCRIPTION); return 0; } if (AppArgs::ExistsOption("version")) { fprintf(stderr, "%s\n", INCROND_VERSION); return 0; } IncronCfg::Init(); std::string cfg; if (!AppArgs::GetOption("config", cfg)) cfg = INCRON_CONFIG; IncronCfg::Load(cfg); std::string lckdir; IncronCfg::GetValue("lockfile_dir", lckdir); std::string lckfile; IncronCfg::GetValue("lockfile_name", lckfile); AppInstance app(lckfile, lckdir); if (AppArgs::ExistsOption("kill")) { fprintf(stderr, "attempting to terminate a running instance of incrond...\n"); if (app.Terminate()) { fprintf(stderr, "the instance notified, going down\n"); return 0; } else { fprintf(stderr, "error - incrond probably not running\n"); return 1; } } if (AppArgs::ExistsOption("foreground")) g_daemon = false; openlog(INCROND_NAME, INCRON_LOG_OPTS, INCRON_LOG_FACIL); syslog(LOG_NOTICE, "starting service (version %s, built on %s %s)", INCRON_VERSION, __DATE__, __TIME__); AppArgs::Destroy(); int ret = 0; std::string sysBase; std::string userBase; if (!IncronCfg::GetValue("system_table_dir", sysBase)) throw InotifyException("configuration is corrupted", EINVAL); if (access(sysBase.c_str(), R_OK) != 0) { syslog(LOG_CRIT, "cannot read directory for system tables (%s): (%i) %s", sysBase.c_str(), errno, strerror(errno)); if (!g_daemon) fprintf(stderr, "cannot read directory for system tables (%s): (%i) %s", sysBase.c_str(), errno, strerror(errno)); ret = 1; goto error; } if (!IncronCfg::GetValue("user_table_dir", userBase)) throw InotifyException("configuration is corrupted", EINVAL); if (access(userBase.c_str(), R_OK) != 0) { syslog(LOG_CRIT, "cannot read directory for user tables (%s): (%i) %s", userBase.c_str(), errno, strerror(errno)); if (!g_daemon) fprintf(stderr, "cannot read directory for user tables (%s): (%i) %s", userBase.c_str(), errno, strerror(errno)); ret = 1; goto error; } try { if (g_daemon) if (daemon(0, 0) == -1) { syslog(LOG_CRIT, "daemonizing failed: (%i) %s", errno, strerror(errno)); fprintf(stderr, "daemonizing failed: (%i) %s\n", errno, strerror(errno)); ret = 1; goto error; } try { if (!app.Lock()) { syslog(LOG_CRIT, "another instance of incrond already running"); if (!g_daemon) fprintf(stderr, "another instance of incrond already running\n"); ret = 1; goto error; } } catch (AppInstException e) { syslog(LOG_CRIT, "instance lookup failed: (%i) %s", e.GetErrorNumber(), strerror(e.GetErrorNumber())); if (!g_daemon) fprintf(stderr, "instance lookup failed: (%i) %s\n", e.GetErrorNumber(), strerror(e.GetErrorNumber())); ret = 1; goto error; } prepare_pipe(); Inotify in; in.SetNonBlock(true); in.SetCloseOnExec(true); uint32_t wm = IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_MOVE | IN_DELETE_SELF | IN_UNMOUNT; InotifyWatch stw(sysBase, wm); in.Add(stw); InotifyWatch utw(userBase, wm); in.Add(utw); EventDispatcher ed(g_cldPipe[0], &in, &stw, &utw); try { load_tables(&ed); } catch (InotifyException e) { int err = e.GetErrorNumber(); syslog(LOG_CRIT, "%s: (%i) %s", e.GetMessage().c_str(), err, strerror(err)); ret = 1; goto error; } ed.Rebuild(); // not too efficient, but simple signal(SIGTERM, on_signal); signal(SIGINT, on_signal); signal(SIGCHLD, on_signal); syslog(LOG_NOTICE, "ready to process filesystem events"); while (!g_fFinish) { int res = poll(ed.GetPollData(), ed.GetSize(), -1); if (res > 0) { if (ed.ProcessEvents()) UserTable::FinishDone(); } else if (res < 0) { switch (errno) { case EINTR: // syscall interrupted - continue polling break; case EAGAIN: // not enough resources - wait a moment and try again syslog(LOG_WARNING, "polling failed due to resource shortage, retrying later..."); sleep(POLL_EAGAIN_WAIT); break; default: throw InotifyException("polling failed", errno, NULL); } } } free_tables(&ed); if (g_cldPipe[0] != -1) close(g_cldPipe[0]); if (g_cldPipe[1] != -1) close(g_cldPipe[1]); } catch (InotifyException e) { int err = e.GetErrorNumber(); syslog(LOG_CRIT, "*** unhandled exception occurred ***"); syslog(LOG_CRIT, " %s", e.GetMessage().c_str()); syslog(LOG_CRIT, " error: (%i) %s", err, strerror(err)); ret = 1; } error: syslog(LOG_NOTICE, "stopping service"); closelog(); return ret; }