Subversion Repositories public

Compare Revisions

Ignore whitespace Rev 46 → Rev 47

/incron/trunk/CHANGELOG
1,2 → 1,9
0.2.0 2006-10-04
* based on inotify-cxx 0.3.1
* better signal handling (no "dirty" wait() in a handler)
* loop avoidance can be used
* more documentation in the code
 
 
0.1.0 2006-09-15
first alpha version
* first alpha version
/incron/trunk/README
69,6 → 69,9
$@ - the watched filesystem path (see above)
$# - the event-related file name
 
The mask may additionaly contain a special symbol IN_NO_LOOP which
disables events occurred during the event handling (to avoid loops).
 
Example: You need to run program 'abc' with the full file path as
an argument every time a file is changed in /var/mail. One of
the solutions follows:
/incron/trunk/incrontab.cpp
23,8 → 23,10
#include "incrontab.h"
 
 
/// Allowed users
#define INCRON_ALLOW_PATH "/etc/incron.allow"
 
#define INCRON_ALLOW_PATH "/etc/incron.allow"
/// Denied users
#define INCRON_DENY_PATH "/etc/incron.deny"
 
 
31,7 → 33,8
 
 
InCronTabEntry::InCronTabEntry()
: m_uMask(0)
: m_uMask(0),
m_fNoLoop(false)
{
}
51,8 → 54,13
std::string m;
InotifyEvent::DumpTypes(m_uMask, m);
if (m.empty())
m = "0";
if (m.empty()) {
m = m_fNoLoop ? "IN_NO_LOOP" : "0";
}
else {
if (m_fNoLoop)
m.append(",IN_NO_LOOP");
}
ss << m_path << " " << m << " " << m_cmd;
return ss.str();
76,7 → 84,11
else {
StringTokenizer tok(s2);
while (tok.HasMoreTokens()) {
rEntry.m_uMask |= InotifyEvent::GetMaskByName(tok.GetNextToken());
std::string s(tok.GetNextToken());
if (s == "IN_NO_LOOP")
rEntry.m_fNoLoop = true;
else
rEntry.m_uMask |= InotifyEvent::GetMaskByName(s);
}
}
/incron/trunk/inotify-cxx.cpp
22,9 → 22,11
 
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
 
#include "inotify-cxx.h"
 
/// dump separator (between particular entries)
#define DUMP_SEP \
({ \
if (!rStr.empty()) { \
32,6 → 34,14
} \
})
 
 
int32_t InotifyEvent::GetDescriptor() const
{
return m_pWatch != NULL // if watch exists
? m_pWatch->GetDescriptor() // return its descriptor
: -1; // else return -1
}
 
uint32_t InotifyEvent::GetMaskByName(const std::string& rName)
{
if (rName == "IN_ACCESS")
163,13 → 173,15
 
void InotifyEvent::DumpTypes(std::string& rStr) const
{
DumpTypes((uint32_t) m_evt.mask, rStr);
DumpTypes(m_uMask, rStr);
}
 
 
Inotify::Inotify()
Inotify::Inotify() throw (InotifyException)
{
m_fd = inotify_init();
m_fd = inotify_init();
if (m_fd == -1)
throw InotifyException(std::string(__PRETTY_FUNCTION__) + ": inotify init failed", errno, NULL);
}
Inotify::~Inotify()
186,31 → 198,31
}
}
 
bool Inotify::Add(InotifyWatch* pWatch)
void Inotify::Add(InotifyWatch* pWatch) throw (InotifyException)
{
if (m_fd == -1)
return false;
throw InotifyException(IN_EXC_MSG("invalid file descriptor"), EBUSY, this);
pWatch->m_wd = inotify_add_watch(m_fd, pWatch->GetPath().c_str(), pWatch->GetMask());
if (pWatch->m_wd != -1) {
m_watches.insert(IN_WATCH_MAP::value_type(pWatch->m_wd, pWatch));
pWatch->m_pInotify = this;
return true;
}
return false;
 
if (pWatch->m_wd == -1)
throw InotifyException(IN_EXC_MSG("adding watch failed"), errno, this);
 
m_watches.insert(IN_WATCH_MAP::value_type(pWatch->m_wd, pWatch));
pWatch->m_pInotify = this;
}
 
void Inotify::Remove(InotifyWatch* pWatch)
void Inotify::Remove(InotifyWatch* pWatch) throw (InotifyException)
{
if (m_fd == -1)
return;
throw InotifyException(IN_EXC_MSG("invalid file descriptor"), EBUSY, this);
if (inotify_rm_watch(m_fd, pWatch->GetMask()) != -1) {
m_watches.erase(pWatch->m_wd);
pWatch->m_wd = -1;
pWatch->m_pInotify = NULL;
}
if (inotify_rm_watch(m_fd, pWatch->GetMask()) == -1)
throw InotifyException(IN_EXC_MSG("removing watch failed"), errno, this);
m_watches.erase(pWatch->m_wd);
pWatch->m_wd = -1;
pWatch->m_pInotify = NULL;
}
 
void Inotify::RemoveAll()
227,7 → 239,7
m_watches.clear();
}
 
bool Inotify::WaitForEvents(bool fNoIntr)
void Inotify::WaitForEvents(bool fNoIntr) throw (InotifyException)
{
ssize_t len = 0;
235,19 → 247,22
len = read(m_fd, m_buf, INOTIFY_BUFLEN);
} while (fNoIntr && len == -1 && errno == EINTR);
if (len <= 0) {
return false;
}
if (errno == EWOULDBLOCK)
return;
if (len < 0)
throw InotifyException(IN_EXC_MSG("reading events failed"), errno, this);
ssize_t i = 0;
while (i < len) {
InotifyWatch* pW = FindWatch(((struct inotify_event *) &m_buf[i])->wd);
InotifyEvent evt((struct inotify_event *) &m_buf[i], pW);
m_events.push_back(evt);
i += INOTIFY_EVENT_SIZE + (int) evt.GetLength();
struct inotify_event* pEvt = (struct inotify_event*) &m_buf[i];
InotifyWatch* pW = FindWatch(pEvt->wd);
if (pW != NULL && pW->IsEnabled()) {
InotifyEvent evt(pEvt, pW);
m_events.push_back(evt);
}
i += INOTIFY_EVENT_SIZE + (ssize_t) pEvt->len;
}
return true;
}
int Inotify::GetEventCount()
255,22 → 270,27
return m_events.size();
}
bool Inotify::GetEvent(InotifyEvent* pEvt)
bool Inotify::GetEvent(InotifyEvent* pEvt) throw (InotifyException)
{
bool b = PeekEvent(pEvt);
bool b = PeekEvent(pEvt);
if (b)
m_events.pop_front();
return b;
}
bool Inotify::PeekEvent(InotifyEvent* pEvt)
bool Inotify::PeekEvent(InotifyEvent* pEvt) throw (InotifyException)
{
if (pEvt == NULL || m_events.empty())
return false;
*pEvt = m_events.front();
if (pEvt == NULL)
throw InotifyException(IN_EXC_MSG("null pointer to event"), EINVAL, this);
return true;
if (!m_events.empty()) {
*pEvt = m_events.front();
return true;
}
return false;
}
 
InotifyWatch* Inotify::FindWatch(int iDescriptor)
282,3 → 302,23
return (*it).second;
}
void Inotify::SetNonBlock(bool fNonBlock) throw (InotifyException)
{
if (m_fd == -1)
throw InotifyException(IN_EXC_MSG("invalid file descriptor"), EBUSY, this);
int res = fcntl(m_fd, F_GETFL);
if (res == -1)
throw InotifyException(IN_EXC_MSG("cannot get inotify flags"), errno, this);
if (fNonBlock) {
res |= O_NONBLOCK;
}
else {
res &= ~O_NONBLOCK;
}
if (fcntl(m_fd, F_SETFL, res) == -1)
throw InotifyException(IN_EXC_MSG("cannot set inotify flags"), errno, this);
}
 
/incron/trunk/icd-main.cpp
20,6 → 20,7
#include <dirent.h>
#include <syslog.h>
#include <errno.h>
#include <sys/poll.h>
 
#include "inotify-cxx.h"
#include "incrontab.h"
26,7 → 27,7
 
#include "usertable.h"
 
#define DAEMON false
#define DAEMON true
 
#define INCRON_APP_NAME "incrond"
#define INCRON_LOG_OPTS (LOG_CONS | LOG_PID)
38,19 → 39,31
 
SUT_MAP g_ut;
 
bool g_fFinish = false;
volatile bool g_fFinish = false;
 
 
/// Handles a signal.
/**
* For SIGTERM and SIGINT it sets the program finish variable.
*
* \param[in] signo signal number
*/
void on_signal(int signo)
{
g_fFinish = true;
if (signo == SIGTERM || signo == SIGINT)
g_fFinish = true;
}
 
void on_child(int signo)
{
wait(NULL);
}
 
/// Checks whether an user exists and has permission to use incron.
/**
* It searches for the given user name in the user database.
* If it failes it returns 'false'. Otherwise it checks
* permission files for this user (see InCronTab::CheckUser()).
*
* \param[in] user user name
* \return true = user has permission to use incron, false = otherwise
*
* \sa InCronTab::CheckUser()
*/
bool check_user(const char* user)
{
struct passwd* pw = getpwnam(user);
60,13 → 73,20
return InCronTab::CheckUser(user);
}
 
bool load_tables(Inotify* pIn, EventDispatcher* pEd)
/// Attempts to load all user incron tables.
/**
* Loaded tables are registered for processing events.
*
* \param[in] pIn inotify object
* \param[in] pEd inotify event dispatcher
*
* \throw InotifyException thrown if base table directory cannot be read
*/
void load_tables(Inotify* pIn, EventDispatcher* pEd) throw (InotifyException)
{
DIR* d = opendir(INCRON_TABLE_BASE);
if (d == NULL) {
syslog(LOG_CRIT, "cannot open table directory: %s", strerror(errno));
return false;
}
if (d == NULL)
throw InotifyException("cannot open table directory", errno);
syslog(LOG_NOTICE, "loading user tables");
87,9 → 107,16
}
closedir(d);
return true;
}
 
/// Main application function.
/**
* \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_APP_NAME, INCRON_LOG_OPTS, INCRON_LOG_FACIL);
96,38 → 123,56
syslog(LOG_NOTICE, "starting service");
Inotify in;
EventDispatcher ed(&in);
if (!load_tables(&in, &ed)) {
closelog();
return 1;
}
signal(SIGTERM, on_signal);
signal(SIGINT, on_signal);
signal(SIGCHLD, on_child);
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;
while (!g_fFinish) {
if (in.WaitForEvents()) {
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 = POLLIN;
pfd.revents = (short) 0;
while (!g_fFinish) {
int res = poll(&pfd, 1, -1);
if (res > 0) {
in.WaitForEvents(true);
UserTable::FinishDone();
}
else if (res < 0) {
if (errno != EINTR)
throw InotifyException("polling failed", errno, NULL);
}
while (in.GetEvent(e)) {
std::string s;
e.DumpTypes(s);
//syslog(LOG_DEBUG, "EVENT: wd=%i, cookie=%u, name=%s, mask: %s", (int) e.GetDescriptor(), (unsigned) e.GetCookie(), e.GetName().c_str(), s.c_str());
if (e.GetWatch() == &watch) {
if (e.IsType(IN_DELETE_SELF) || e.IsType(IN_UNMOUNT)) {
syslog(LOG_CRIT, "base directory destroyed, exitting");
163,6 → 208,11
}
}
}
} 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");
/incron/trunk/incrontab.h
22,89 → 22,183
 
#include "strtok.h"
 
 
/// Incron table base directory
#define INCRON_TABLE_BASE "/var/spool/incron/"
 
 
 
/// Incron table entry class.
class InCronTabEntry
{
public:
/// Constructor.
/**
* Creates an empty entry for later use with Parse().
*
* \sa Parse()
*/
InCronTabEntry();
 
/// Constructor.
/**
* Creates an entry based on defined parameters.
*
* \param[in] rPath watched filesystem path
* \param[in] uMask event mask
* \param[in] rCmd command string
*/
InCronTabEntry(const std::string& rPath, uint32_t uMask, const std::string& rCmd);
/// Destructor.
~InCronTabEntry() {}
/// Converts the entry to string representation.
/**
* This method creates a string for use in a table file.
*
* \return string representation
*/
std::string ToString() const;
/// Parses a string and attempts to extract entry parameters.
/**
* \param[in] rStr parsed string
* \param[out] rEntry parametrized entry
* \return true = success, false = failure
*/
static bool Parse(const std::string& rStr, InCronTabEntry& rEntry);
/// Returns the watch filesystem path.
/**
* \return watch path
*/
inline const std::string& GetPath() const
{
return m_path;
}
/// Returns the event mask.
/**
* \return event mask
*/
inline int32_t GetMask() const
{
return m_uMask;
}
/// Returns the command string.
/**
* \return command string
*/
inline const std::string& GetCmd() const
{
return m_cmd;
}
/// Checks whether this entry has set loop-avoidance.
/**
* \return true = no loop, false = loop allowed
*/
inline bool IsNoLoop() const
{
return m_fNoLoop;
}
protected:
std::string m_path;
uint32_t m_uMask;
std::string m_cmd;
std::string m_path; ///< watch path
uint32_t m_uMask; ///< event mask
std::string m_cmd; ///< command string
bool m_fNoLoop; ///< no loop yes/no
};
 
 
 
/// Incron table class.
class InCronTab
{
public:
/// Constructor.
InCronTab() {}
/// Destructor.
~InCronTab() {}
/// Add an entry to the table.
/**
* \param[in] rEntry table entry
*/
inline void Add(const InCronTabEntry& rEntry)
{
m_tab.push_back(rEntry);
}
/// Removes all entries.
inline void Clear()
{
m_tab.clear();
}
/// Checks whether the table is empty.
/**
* \return true = empty, false = otherwise
*/
inline bool IsEmpty() const
{
return m_tab.empty();
}
/// Returns the count of entries.
/**
* \return count of entries
*/
inline int GetCount() const
{
return (int) m_tab.size();
}
/// Returns an entry.
/**
* \return reference to the entry for the given index
*
* \attention This method doesn't test index bounds. If you
* pass an invalid value the program may crash
* and/or behave unpredictible way!
*/
inline InCronTabEntry& GetEntry(int index)
{
return m_tab[index];
}
/// Loads the table.
/**
* \param[in] rPath path to a source table file
* \return true = success, false = failure
*/
bool Load(const std::string& rPath);
/// Saves the table.
/**
* \param[in] rPath path to a destination table file
* \return true = success, false = failure
*/
bool Save(const std::string& rPath);
/// Checks whether an user has permission to use incron.
/**
* \param[in] rUser user name
* \return true = permission OK, false = otherwise
*/
static bool CheckUser(const std::string& rUser);
/// Composes a path to an user incron table file.
/**
* \param[in] rUser user name
* \return path to the table file
*
* \attention No tests (existence, permission etc.) are done.
*/
static std::string GetUserTablePath(const std::string& rUser);
 
protected:
std::deque<InCronTabEntry> m_tab;
std::deque<InCronTabEntry> m_tab; ///< incron table
};
 
 
/incron/trunk/usertable.cpp
17,10 → 17,20
#include <pwd.h>
#include <syslog.h>
#include <errno.h>
#include <sys/wait.h>
 
#include "usertable.h"
 
 
PROC_LIST UserTable::s_procList;
 
 
void on_proc_done(InotifyWatch* pW)
{
pW->SetEnabled(true);
}
 
 
EventDispatcher::EventDispatcher(Inotify* pIn)
{
m_pIn = pIn;
104,9 → 114,14
for (int i=0; i<cnt; i++) {
InCronTabEntry& rE = m_tab.GetEntry(i);
InotifyWatch* pW = new InotifyWatch(rE.GetPath(), rE.GetMask());
m_pIn->Add(pW);
m_pEd->Register(pW, this);
m_map.insert(IWCE_MAP::value_type(pW, &rE));
try {
m_pIn->Add(pW);
m_pEd->Register(pW, this);
m_map.insert(IWCE_MAP::value_type(pW, &rE));
} catch (InotifyException e) {
syslog(LOG_ERR, "cannot create watch for user %s", m_user.c_str());
delete pW;
}
}
}
 
175,8 → 190,12
syslog(LOG_INFO, "(%s) CMD (%s)", m_user.c_str(), cmd.c_str());
pid_t pid = fork();
if (pid == 0) {
if (pE->IsNoLoop())
pW->SetEnabled(false);
ProcData_t pd;
pd.pid = fork();
if (pd.pid == 0) {
struct passwd* pwd = getpwnam(m_user.c_str());
if (pwd == NULL)
189,10 → 208,22
_exit(1);
}
}
else if (pid > 0) {
else if (pd.pid > 0) {
if (pE->IsNoLoop()) {
pd.onDone = on_proc_done;
pd.pWatch = pW;
}
else {
pd.onDone = NULL;
pd.pWatch = NULL;
}
s_procList.push_back(pd);
}
else {
if (pE->IsNoLoop())
pW->SetEnabled(true);
syslog(LOG_ERR, "cannot fork process: %s", strerror(errno));
}
}
234,3 → 265,22
return true;
}
 
void UserTable::FinishDone()
{
PROC_LIST::iterator it = s_procList.begin();
while (it != s_procList.end()) {
ProcData_t& pd = *it;
int status = 0;
int res = waitpid(pd.pid, &status, WNOHANG);
if (res == pd.pid && (WIFEXITED(status) || WIFSIGNALED(status))) {
if (pd.onDone != NULL)
(*pd.onDone)(pd.pWatch);
it = s_procList.erase(it);
}
else {
it++;
}
}
}
 
 
/incron/trunk/inotify-cxx.h
40,12 → 40,72
/// Event buffer length
#define INOTIFY_BUFLEN (1024 * (INOTIFY_EVENT_SIZE + 16))
 
/// Helper macro for creating exception messages.
/**
* It prepends the message by the function name.
*/
#define IN_EXC_MSG(msg) (std::string(__PRETTY_FUNCTION__) + ": " + msg)
 
 
 
// forward declaration
class InotifyWatch;
class Inotify;
 
 
/// Class for inotify exceptions
class InotifyException
{
public:
/// Constructor
/**
* \param[in] rMsg message
* \param[in] iErr error number (see errno.h)
* \param[in] pSrc source
*/
InotifyException(const std::string& rMsg = "", int iErr = 0, void* pSrc = NULL)
: m_msg(rMsg),
m_err(iErr)
{
m_pSrc = pSrc;
}
/// Returns the exception message.
/**
* \return message
*/
inline const std::string& GetMessage() const
{
return m_msg;
}
/// Returns the exception error number.
/**
* If not applicable this value is 0 (zero).
*
* \return error number (standardized; see errno.h)
*/
inline int GetErrorNumber() const
{
return m_err;
}
/// Returns the exception source.
/**
* \return source
*/
inline void* GetSource() const
{
return m_pSrc;
}
 
protected:
std::string m_msg; ///< message
int m_err; ///< error number
mutable void* m_pSrc; ///< source
};
 
 
/// inotify event class
/**
* It holds all information about inotify event and provides
59,9 → 119,9
* Creates a plain event.
*/
InotifyEvent()
: m_uMask(0),
m_uCookie(0)
{
memset(&m_evt, 0, sizeof(m_evt));
m_evt.wd = (int32_t) -1;
m_pWatch = NULL;
}
74,16 → 134,17
* \param[in] pWatch inotify watch
*/
InotifyEvent(const struct inotify_event* pEvt, InotifyWatch* pWatch)
: m_uMask(0),
m_uCookie(0)
{
if (pEvt != NULL) {
memcpy(&m_evt, pEvt, sizeof(m_evt));
m_uMask = (uint32_t) pEvt->mask;
m_uCookie = (uint32_t) pEvt->cookie;
if (pEvt->name != NULL)
m_name = pEvt->name;
m_pWatch = pWatch;
}
else {
memset(&m_evt, 0, sizeof(m_evt));
m_evt.wd = (int32_t) -1;
m_pWatch = NULL;
}
}
97,10 → 158,7
*
* \sa InotifyWatch::GetDescriptor()
*/
inline int32_t GetDescriptor() const
{
return (int32_t) m_evt.wd;
}
int32_t GetDescriptor() const;
/// Returns the event mask.
/**
110,7 → 168,7
*/
inline uint32_t GetMask() const
{
return (uint32_t) m_evt.mask;
return m_uMask;
}
/// Checks a value for the event type.
131,7 → 189,7
*/
inline bool IsType(uint32_t uType) const
{
return IsType((uint32_t) m_evt.mask, uType);
return IsType(m_uMask, uType);
}
/// Returns the event cookie.
140,7 → 198,7
*/
inline uint32_t GetCookie() const
{
return (uint32_t) m_evt.cookie;
return m_uCookie;
}
/// Returns the event name length.
149,7 → 207,7
*/
inline uint32_t GetLength() const
{
return (uint32_t) m_evt.len;
return (uint32_t) m_name.length();
}
/// Returns the event name.
179,27 → 237,6
return m_pWatch;
}
/// Returns the event raw data.
/**
* For NULL pointer it does nothing.
*
* \param[in,out] pEvt event data
*/
inline void GetData(struct inotify_event* pEvt)
{
if (pEvt != NULL)
memcpy(pEvt, &m_evt, sizeof(m_evt));
}
/// Returns the event raw data.
/**
* \param[in,out] rEvt event data
*/
inline void GetData(struct inotify_event& rEvt)
{
memcpy(&rEvt, &m_evt, sizeof(m_evt));
}
/// Finds the appropriate mask for a name.
/**
* \param[in] rName mask name
221,8 → 258,9
void DumpTypes(std::string& rStr) const;
private:
struct inotify_event m_evt; ///< event structure
std::string m_name; ///< event name
uint32_t m_uMask; ///< mask
uint32_t m_uCookie; ///< cookie
std::string m_name; ///< name
InotifyWatch* m_pWatch; ///< source watch
};
 
239,12 → 277,15
*
* \param[in] rPath watched file path
* \param[in] uMask mask for events
* \param[in] fEnabled events enabled yes/no
*/
InotifyWatch(const std::string& rPath, int32_t uMask)
InotifyWatch(const std::string& rPath, int32_t uMask, bool fEnabled = true)
: m_path(rPath),
m_uMask(uMask),
m_wd((int32_t) -1),
m_fEnabled(fEnabled)
{
m_path = rPath;
m_uMask = uMask;
m_wd = (int32_t) -1;
}
/// Destructor.
286,6 → 327,16
return m_pInotify;
}
inline void SetEnabled(bool fEnabled)
{
m_fEnabled = fEnabled;
}
inline bool IsEnabled() const
{
return m_fEnabled;
}
private:
friend class Inotify;
 
293,6 → 344,7
uint32_t m_uMask; ///< event mask
int32_t m_wd; ///< watch descriptor
Inotify* m_pInotify; ///< inotify object
bool m_fEnabled; ///< events enabled yes/no
};
 
 
308,42 → 360,37
/**
* Creates and initializes an instance of inotify communication
* object (opens the inotify device).
*
* \throw InotifyException thrown if inotify isn't available
*/
Inotify();
Inotify() throw (InotifyException);
/// Destructor.
/**
* Calls Close() due for clean-up.
* Calls Close() due to clean-up.
*/
~Inotify();
/// Removes all watches and closes the inotify device.
void Close();
/// Checks whether the inotify is ready.
/**
* \return true = initialized properly, false = something failed
*/
inline bool IsReady() const
{
return m_fd != -1;
}
/// Adds a new watch.
/**
* \param[in] pWatch inotify watch
* \return true = success, false = failure
*
* \throw InotifyException thrown if adding failed
*/
bool Add(InotifyWatch* pWatch);
void Add(InotifyWatch* pWatch) throw (InotifyException);
/// Adds a new watch.
/**
* \param[in] rWatch inotify watch
* \return true = success, false = failure
*
* \throw InotifyException thrown if adding failed
*/
inline bool Add(InotifyWatch& rWatch)
inline void Add(InotifyWatch& rWatch) throw (InotifyException)
{
return Add(&rWatch);
Add(&rWatch);
}
/// Removes a watch.
351,8 → 398,10
* If the given watch is not present it does nothing.
*
* \param[in] pWatch inotify watch
*
* \throw InotifyException thrown if removing failed
*/
void Remove(InotifyWatch* pWatch);
void Remove(InotifyWatch* pWatch) throw (InotifyException);
/// Removes a watch.
/**
359,8 → 408,10
* If the given watch is not present it does nothing.
*
* \param[in] rWatch inotify watch
*
* \throw InotifyException thrown if removing failed
*/
inline void Remove(InotifyWatch& rWatch)
inline void Remove(InotifyWatch& rWatch) throw (InotifyException)
{
Remove(&rWatch);
}
379,12 → 430,17
/// Waits for inotify events.
/**
* It waits until one or more events occur.
* It waits until one or more events occur. When called
* in nonblocking mode it only retrieves occurred events
* to the internal queue and exits.
*
* \param[in] fNoIntr if true it re-calls the system call after a handled signal
* \return true = event(s) occurred, false = failure
*
* \throw InotifyException thrown if reading events failed
*
* \sa SetNonBlock()
*/
bool WaitForEvents(bool fNoIntr = false);
void WaitForEvents(bool fNoIntr = false) throw (InotifyException);
/// Returns the count of received and queued events.
/**
401,9 → 457,10
* If the pointer is NULL it does nothing.
*
* \param[in,out] pEvt event object
* \return true = success, false = failure
*
* \throw InotifyException thrown if the provided pointer is NULL
*/
bool GetEvent(InotifyEvent* pEvt);
bool GetEvent(InotifyEvent* pEvt) throw (InotifyException);
/// Extracts a queued inotify event.
/**
410,9 → 467,10
* The extracted event is removed from the queue.
*
* \param[in,out] rEvt event object
* \return true = success, false = failure
*
* \throw InotifyException thrown only in very anomalous cases
*/
bool GetEvent(InotifyEvent& rEvt)
bool GetEvent(InotifyEvent& rEvt) throw (InotifyException)
{
return GetEvent(&rEvt);
}
423,9 → 481,10
* If the pointer is NULL it does nothing.
*
* \param[in,out] pEvt event object
* \return true = success, false = failure
*
* \throw InotifyException thrown if the provided pointer is NULL
*/
bool PeekEvent(InotifyEvent* pEvt);
bool PeekEvent(InotifyEvent* pEvt) throw (InotifyException);
/// Extracts a queued inotify event (without removing).
/**
432,9 → 491,10
* The extracted event stays in the queue.
*
* \param[in,out] rEvt event object
* \return true = success, false = failure
*
* \throw InotifyException thrown only in very anomalous cases
*/
bool PeekEvent(InotifyEvent& rEvt)
bool PeekEvent(InotifyEvent& rEvt) throw (InotifyException)
{
return PeekEvent(&rEvt);
}
446,7 → 506,35
* \param[in] iDescriptor watch descriptor
* \return found descriptor; NULL if no such watch exists
*/
InotifyWatch* FindWatch(int iDescriptor);
InotifyWatch* FindWatch(int iDescriptor);
/// Returns the file descriptor.
/**
* The descriptor can be used in standard low-level file
* functions (poll(), select(), fcntl() etc.).
*
* \return valid file descriptor or -1 for inactive object
*
* \sa SetNonBlock()
*/
inline int GetDescriptor() const
{
return m_fd;
}
/// Enables/disables non-blocking mode.
/**
* Use this mode if you want to monitor the descriptor
* (acquired thru GetDescriptor()) in functions such as
* poll(), select() etc.
*
* \param[in] fNonBlock enable/disable non-blocking mode
*
* \throw InotifyException thrown if setting mode failed
*
* \sa GetDescriptor()
*/
void SetNonBlock(bool fNonBlock) throw (InotifyException);
 
private:
int m_fd; ///< file descriptor
/incron/trunk/strtok.h
26,31 → 26,74
 
#include <string>
 
 
/// Simple string tokenizer class.
/**
* This class implements a string tokenizer. It splits a string
* by a character to a number of elements (tokens) which are
* provided sequentially.
*
* All operations are made on the original string itself.
* The implementation is not ready to handle any changes of the
* string.
*
* The original string is left unchanged. All tokens are returned
* as newly created strings.
*/
class StringTokenizer
{
public:
/// Constructor.
/**
* Creates a ready-to-use tokenizer.
*
* \param[in] rStr string for tokenizing
* \param[in] cDelim delimiter (separator) character
*/
StringTokenizer(const std::string& rStr, char cDelim = ',');
/// Destructor.
~StringTokenizer() {}
/// Checks whether the tokenizer can provide more tokens.
/**
* \return true = more tokens available, false = otherwise
*/
inline bool HasMoreTokens() const
{
return m_pos < m_len;
}
/// Returns the next token.
/**
* \return next token or "" if no more tokens available
*/
std::string GetNextToken();
/// Sets a delimiter (separator) character.
/**
* The new delimiter has effect only to tokens returned later;
* the position in the string is not affected.
*
* \param[in] cDelim delimiter character
*/
inline void SetDelimiter(char cDelim)
{
m_cDelim = cDelim;
}
/// Returns the delimiter (separator) character.
/**
* \return delimiter character
*/
inline char GetDelimiter() const
{
return m_cDelim;
}
/// Resets the tokenizer.
/**
* Re-initializes tokenizing to the start of the string.
*/
inline void Reset()
{
m_pos = 0;
57,10 → 100,10
}
private:
std::string m_str;
char m_cDelim;
std::string::size_type m_pos;
std::string::size_type m_len;
std::string m_str; ///< tokenized string
char m_cDelim; ///< delimiter character
std::string::size_type m_pos; ///< current position
std::string::size_type m_len; ///< string length
};
 
 
/incron/trunk/usertable.h
17,6 → 17,7
#define _USERTABLE_H_
 
#include <map>
#include <deque>
 
#include "inotify-cxx.h"
#include "incrontab.h"
24,54 → 25,152
 
class UserTable;
 
/// Callback for calling after a process finishes.
typedef void (*proc_done_cb)(InotifyWatch*);
 
/// Child process data
typedef struct
{
pid_t pid; ///< PID
proc_done_cb onDone; ///< function called after process finishes
InotifyWatch* pWatch; ///< related watch
} ProcData_t;
 
/// Watch-to-usertable mapping
typedef std::map<InotifyWatch*, UserTable*> IWUT_MAP;
 
/// Watch-to-tableentry mapping
typedef std::map<InotifyWatch*, InCronTabEntry*> IWCE_MAP;
 
/// Child process list
typedef std::deque<ProcData_t> PROC_LIST;
 
/// Event dispatcher class.
/**
* This class distributes inotify events to appropriate user tables.
*/
class EventDispatcher
{
public:
/// Constructor.
/**
* \param[in] pIn inotify object
*/
EventDispatcher(Inotify* pIn);
/// Destructor.
~EventDispatcher() {}
 
/// Dispatches an event.
/**
* \param[in] rEvt inotify event
*/
void DispatchEvent(InotifyEvent& rEvt);
/// Registers a watch for an user table.
/**
* \param[in] pWatch inotify watch
* \param[in] pTab user table
*/
void Register(InotifyWatch* pWatch, UserTable* pTab);
/// Unregisters a watch.
/**
* \param[in] pWatch inotify watch
*/
void Unregister(InotifyWatch* pWatch);
/// Unregisters all watches for an user table.
/**
* \param[in] pTab user table
*/
void UnregisterAll(UserTable* pTab);
private:
Inotify* m_pIn;
IWUT_MAP m_maps;
Inotify* m_pIn; ///< inotify object
IWUT_MAP m_maps; ///< watch-to-usertable mapping
/// Finds an user table for a watch.
/**
* \param[in] pW inotify watch
* \return pointer to the appropriate watch; NULL if no such watch exists
*/
UserTable* FindTable(InotifyWatch* pW);
};
 
 
/// User table class.
/**
* This class processes inotify events for an user. It creates
* child processes which do appropriate actions as defined
* in the user table file.
*/
class UserTable
{
public:
/// Constructor.
/**
* \param[in] pIn inotify object
* \param[in] pEd event dispatcher
* \param[in] rUser user name
*/
UserTable(Inotify* pIn, EventDispatcher* pEd, const std::string& rUser);
/// Destructor.
virtual ~UserTable();
/// Loads the table.
/**
* All loaded entries have their inotify watches and are
* registered for event dispatching.
* If loading fails the table remains empty.
*/
void Load();
/// Removes all entries from the table.
/**
* All entries are unregistered from the event dispatcher and
* their watches are destroyed.
*/
void Dispose();
/// Processes an inotify event.
/**
* \param[in] rEvt inotify event
*/
void OnEvent(InotifyEvent& rEvt);
/// Cleans-up all zombie child processes and enables disabled watches.
/**
* \attention This method must be called AFTER processing all events
* which has been caused by the processes.
*/
static void FinishDone();
private:
Inotify* m_pIn;
EventDispatcher* m_pEd;
std::string m_user;
InCronTab m_tab;
IWCE_MAP m_map;
Inotify* m_pIn; ///< inotify object
EventDispatcher* m_pEd; ///< event dispatcher
std::string m_user; ///< user name
InCronTab m_tab; ///< incron table
IWCE_MAP m_map; ///< watch-to-entry mapping
 
static PROC_LIST s_procList; ///< child process list
/// Finds an entry for a watch.
/**
* \param[in] pWatch inotify watch
* \return pointer to the appropriate entry; NULL if no such entry exists
*/
InCronTabEntry* FindEntry(InotifyWatch* pWatch);
/// Prepares arguments for creating a child process.
/**
* \param[in] rCmd command string
* \param[out] argc argument count
* \param[out] argv argument array
* \return true = success, false = failure
*/
bool PrepareArgs(const std::string& rCmd, int& argc, char**& argv);
};
 
#endif //_USERTABLE_H_