Subversion Repositories public

Compare Revisions

Ignore whitespace Rev 50 → Rev 51

/incron/trunk/ict-main.cpp
29,7 → 29,7
#define INCRON_DEFAULT_EDITOR "vim"
 
 
const char* argp_program_version = "incrontab 0.3.0";
const char* argp_program_version = "incrontab 0.2.2";
const char* argp_program_bug_address = "<bugs@aiken.cz>";
 
static char doc[] = "Table manipulator for incrond (inotify cron daemon)";
/incron/trunk/CHANGELOG
1,3 → 1,9
0.2.2 2006-10-29
* based on inotify-cxx 0.5.0
* 'uncleaned children' bug has been fixed (#0000095)
* memory leak (child process arguments) has been fixed (#0000096)
 
 
0.2.1 2006-10-14
* based on inotify-cxx 0.4.1 (it should also fix crashing as described in #0000085)
* build failure (due to missing 'useradd -M') on some distributions has been fixed
/incron/trunk/LICENSE-X11
1,25 → 1,22
Copyright (C) 1996 X Consortium
Copyright (c) 2006 Lukas Jelinek
 
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
 
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
 
Except as contained in this notice, the name of the X Consortium shall
not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization from
the X Consortium.
/incron/trunk/inotify-cxx.cpp
203,30 → 203,44
 
void InotifyWatch::SetMask(uint32_t uMask) throw (InotifyException)
{
IN_WRITE_BEGIN
if (m_wd != -1) {
int wd = inotify_add_watch(m_pInotify->GetDescriptor(), m_path.c_str(), uMask);
if (wd != m_wd)
if (wd != m_wd) {
IN_WRITE_END_NOTHROW
throw InotifyException(IN_EXC_MSG("changing mask failed"), wd == -1 ? errno : EINVAL, this);
}
}
m_uMask = uMask;
IN_WRITE_END
}
 
void InotifyWatch::SetEnabled(bool fEnabled) throw (InotifyException)
{
if (fEnabled == m_fEnabled)
IN_WRITE_BEGIN
if (fEnabled == m_fEnabled) {
IN_WRITE_END_NOTHROW
return;
}
if (m_pInotify != NULL) {
if (fEnabled) {
m_wd = inotify_add_watch(m_pInotify->GetDescriptor(), m_path.c_str(), m_uMask);
if (m_wd == -1)
if (m_wd == -1) {
IN_WRITE_END_NOTHROW
throw InotifyException(IN_EXC_MSG("enabling watch failed"), errno, this);
}
m_pInotify->m_watches.insert(IN_WATCH_MAP::value_type(m_wd, this));
}
else {
if (inotify_rm_watch(m_pInotify->GetDescriptor(), m_wd) != 0)
if (inotify_rm_watch(m_pInotify->GetDescriptor(), m_wd) != 0) {
IN_WRITE_END_NOTHROW
throw InotifyException(IN_EXC_MSG("disabling watch failed"), errno, this);
}
m_pInotify->m_watches.erase(m_wd);
m_wd = -1;
}
233,39 → 247,57
}
m_fEnabled = fEnabled;
IN_WRITE_END
}
 
 
Inotify::Inotify() throw (InotifyException)
{
IN_LOCK_INIT
m_fd = inotify_init();
if (m_fd == -1)
if (m_fd == -1) {
IN_LOCK_DONE
throw InotifyException(IN_EXC_MSG("inotify init failed"), errno, NULL);
}
}
Inotify::~Inotify()
{
Close();
IN_LOCK_DONE
}
 
void Inotify::Close()
{
IN_WRITE_BEGIN
if (m_fd != -1) {
RemoveAll();
close(m_fd);
m_fd = -1;
}
IN_WRITE_END
}
 
void Inotify::Add(InotifyWatch* pWatch) throw (InotifyException)
{
IN_WRITE_BEGIN
// invalid descriptor - this case shouldn't occur - go away
if (m_fd == -1)
if (m_fd == -1) {
IN_WRITE_END_NOTHROW
throw InotifyException(IN_EXC_MSG("invalid file descriptor"), EBUSY, this);
}
 
// this path already watched - go away
if (FindWatch(pWatch->GetPath()) != NULL)
if (FindWatch(pWatch->GetPath()) != NULL) {
IN_WRITE_END_NOTHROW
throw InotifyException(IN_EXC_MSG("path already watched"), EBUSY, this);
}
// for enabled watch
if (pWatch->IsEnabled()) {
274,8 → 306,10
int wd = inotify_add_watch(m_fd, pWatch->GetPath().c_str(), pWatch->GetMask());
// adding failed - go away
if (wd == -1)
if (wd == -1) {
IN_WRITE_END_NOTHROW
throw InotifyException(IN_EXC_MSG("adding watch failed"), errno, this);
}
// this path already watched (but defined another way)
InotifyWatch* pW = FindWatch(wd);
283,10 → 317,12
// try to recover old watch because it may be modified - then go away
if (inotify_add_watch(m_fd, pW->GetPath().c_str(), pW->GetMask()) < 0) {
IN_WRITE_END_NOTHROW
throw InotifyException(IN_EXC_MSG("watch collision detected and recovery failed"), errno, this);
}
else {
// recovery failed - go away
IN_WRITE_END_NOTHROW
throw InotifyException(IN_EXC_MSG("path already watched (but defined another way)"), EBUSY, this);
}
}
297,20 → 333,28
m_paths.insert(IN_WP_MAP::value_type(pWatch->m_path, pWatch));
pWatch->m_pInotify = this;
IN_WRITE_END
}
 
void Inotify::Remove(InotifyWatch* pWatch) throw (InotifyException)
{
IN_WRITE_BEGIN
// invalid descriptor - this case shouldn't occur - go away
if (m_fd == -1)
if (m_fd == -1) {
IN_WRITE_END_NOTHROW
throw InotifyException(IN_EXC_MSG("invalid file descriptor"), EBUSY, this);
}
// for enabled watch
if (pWatch->m_wd != -1) {
// removing watch failed - go away
if (inotify_rm_watch(m_fd, pWatch->m_wd) == -1)
if (inotify_rm_watch(m_fd, pWatch->m_wd) == -1) {
IN_WRITE_END_NOTHROW
throw InotifyException(IN_EXC_MSG("removing watch failed"), errno, this);
}
m_watches.erase(pWatch->m_wd);
pWatch->m_wd = -1;
}
317,10 → 361,14
 
m_paths.erase(pWatch->m_path);
pWatch->m_pInotify = NULL;
IN_WRITE_END
}
 
void Inotify::RemoveAll()
{
IN_WRITE_BEGIN
IN_WP_MAP::iterator it = m_paths.begin();
while (it != m_paths.end()) {
InotifyWatch* pW = (*it).second;
334,6 → 382,8
m_watches.clear();
m_paths.clear();
IN_WRITE_END
}
 
void Inotify::WaitForEvents(bool fNoIntr) throw (InotifyException)
350,6 → 400,8
if (len < 0)
throw InotifyException(IN_EXC_MSG("reading events failed"), errno, this);
IN_WRITE_BEGIN
ssize_t i = 0;
while (i < len) {
struct inotify_event* pEvt = (struct inotify_event*) &m_buf[i];
360,19 → 412,24
}
i += INOTIFY_EVENT_SIZE + (ssize_t) pEvt->len;
}
}
int Inotify::GetEventCount()
{
return m_events.size();
IN_WRITE_END
}
bool Inotify::GetEvent(InotifyEvent* pEvt) throw (InotifyException)
{
bool b = PeekEvent(pEvt);
if (pEvt == NULL)
throw InotifyException(IN_EXC_MSG("null pointer to event"), EINVAL, this);
if (b)
IN_WRITE_BEGIN
bool b = !m_events.empty();
if (b) {
*pEvt = m_events.front();
m_events.pop_front();
}
IN_WRITE_END
return b;
}
382,40 → 439,56
if (pEvt == NULL)
throw InotifyException(IN_EXC_MSG("null pointer to event"), EINVAL, this);
if (!m_events.empty()) {
IN_READ_BEGIN
bool b = !m_events.empty();
if (b) {
*pEvt = m_events.front();
return true;
}
return false;
IN_READ_END
return b;
}
 
InotifyWatch* Inotify::FindWatch(int iDescriptor)
{
IN_READ_BEGIN
IN_WATCH_MAP::iterator it = m_watches.find(iDescriptor);
if (it == m_watches.end())
return NULL;
return (*it).second;
InotifyWatch* pW = it == m_watches.end() ? NULL : (*it).second;
IN_READ_END
return pW;
}
 
InotifyWatch* Inotify::FindWatch(const std::string& rPath)
{
IN_READ_BEGIN
IN_WP_MAP::iterator it = m_paths.find(rPath);
if (it == m_paths.end())
return NULL;
InotifyWatch* pW = it == m_paths.end() ? NULL : (*it).second;
IN_READ_END
return (*it).second;
return pW;
}
void Inotify::SetNonBlock(bool fNonBlock) throw (InotifyException)
{
if (m_fd == -1)
IN_WRITE_BEGIN
if (m_fd == -1) {
IN_WRITE_END_NOTHROW
throw InotifyException(IN_EXC_MSG("invalid file descriptor"), EBUSY, this);
}
int res = fcntl(m_fd, F_GETFL);
if (res == -1)
if (res == -1) {
IN_WRITE_END_NOTHROW
throw InotifyException(IN_EXC_MSG("cannot get inotify flags"), errno, this);
}
if (fNonBlock) {
res |= O_NONBLOCK;
424,7 → 497,11
res &= ~O_NONBLOCK;
}
if (fcntl(m_fd, F_SETFL, res) == -1)
if (fcntl(m_fd, F_SETFL, res) == -1) {
IN_WRITE_END_NOTHROW
throw InotifyException(IN_EXC_MSG("cannot set inotify flags"), errno, this);
}
IN_WRITE_END
}
 
/incron/trunk/icd-main.cpp
173,7 → 173,6
int res = poll(&pfd, 1, -1);
if (res > 0) {
in.WaitForEvents(true);
UserTable::FinishDone();
}
else if (res < 0) {
if (errno != EINTR)
180,6 → 179,8
throw InotifyException("polling failed", errno, NULL);
}
UserTable::FinishDone();
while (in.GetEvent(e)) {
if (e.GetWatch() == &watch) {
/incron/trunk/TODO
0,0 → 1,3
Currently pending tasks:
 
*** nothing to do ***
/incron/trunk/usertable.cpp
198,14 → 198,11
if (pd.pid == 0) {
struct passwd* pwd = getpwnam(m_user.c_str());
if (pwd == NULL)
if ( pwd == NULL // user not found
|| setuid(pwd->pw_uid) != 0 // setting UID failed
|| execvp(argv[0], argv) != 0) // exec failed
{
_exit(1);
if (setuid(pwd->pw_uid) != 0)
_exit(1);
if (execvp(argv[0], argv) != 0) {
_exit(1);
}
}
else if (pd.pid > 0) {
226,6 → 223,8
syslog(LOG_ERR, "cannot fork process: %s", strerror(errno));
}
CleanupArgs(argc, argv);
}
 
InCronTabEntry* UserTable::FindEntry(InotifyWatch* pWatch)
265,6 → 264,15
return true;
}
 
void UserTable::CleanupArgs(int argc, char** argv)
{
for (int i=0; i<argc; i++) {
delete[] argv[i];
}
delete[] argv;
}
 
void UserTable::FinishDone()
{
PROC_LIST::iterator it = s_procList.begin();
/incron/trunk/inotify-cxx.h
50,8 → 50,89
*/
#define IN_EXC_MSG(msg) (std::string(__PRETTY_FUNCTION__) + ": " + msg)
 
/// inotify-cxx thread safety
/**
* If this symbol is defined you can use this interface safely
* threaded applications. Remember that it slightly degrades
* performance.
*
* Even if INOTIFY_THREAD_SAFE is defined some classes stay
* unsafe. If you must use them (must you?) in more than one
* thread concurrently you need to implement explicite locking.
*
* You need not to define INOTIFY_THREAD_SAFE in that cases
* where the application is multithreaded but all the inotify
* infrastructure will be managed only in one thread. This is
* the recommended way.
*
* Locking may fail (it is very rare but not impossible). In this
* case an exception is thrown. But if unlocking fails in case
* of an error it does nothing (this failure is ignored).
*/
#ifdef INOTIFY_THREAD_SAFE
 
#include <pthread.h>
 
#define IN_LOCK_DECL mutable pthread_rwlock_t __m_lock;
 
#define IN_LOCK_INIT \
{ \
pthread_rwlockattr_t attr; \
int res = 0; \
if ((res = pthread_rwlockattr_init(&attr)) != 0) \
throw InotifyException(IN_EXC_MSG("cannot initialize lock attributes"), res, this); \
if ((res = pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NP)) != 0) \
throw InotifyException(IN_EXC_MSG("cannot set lock kind"), res, this); \
if ((res = pthread_rwlock_init(&__m_lock, &attr)) != 0) \
throw InotifyException(IN_EXC_MSG("cannot initialize lock"), res, this); \
pthread_rwlockattr_destroy(&attr); \
}
#define IN_LOCK_DONE pthread_rwlock_destroy(&__m_lock);
 
#define IN_READ_BEGIN \
{ \
int res = pthread_rwlock_rdlock(&__m_lock); \
if (res != 0) \
throw InotifyException(IN_EXC_MSG("locking for reading failed"), res, (void*) this); \
}
#define IN_READ_END \
{ \
int res = pthread_rwlock_unlock(&__m_lock); \
if (res != 0) \
throw InotifyException(IN_EXC_MSG("unlocking failed"), res, (void*) this); \
}
#define IN_READ_END_NOTHROW pthread_rwlock_unlock(&__m_lock);
#define IN_WRITE_BEGIN \
{ \
int res = pthread_rwlock_wrlock(&__m_lock); \
if (res != 0) \
throw InotifyException(IN_EXC_MSG("locking for writing failed"), res, (void*) this); \
}
#define IN_WRITE_END IN_READ_END
#define IN_WRITE_END_NOTHROW IN_READ_END_NOTHROW
 
#else // INOTIFY_THREAD_SAFE
 
#define IN_LOCK_DECL
#define IN_LOCK_INIT
#define IN_LOCK_DONE
#define IN_READ_BEGIN
#define IN_READ_END
#define IN_READ_END_NOTHROW
#define IN_WRITE_BEGIN
#define IN_WRITE_END
#define IN_WRITE_END_NOTHROW
 
#endif // INOTIFY_THREAD_SAFE
 
 
 
 
// forward declaration
class InotifyWatch;
class Inotify;
58,6 → 139,14
 
 
/// Class for inotify exceptions
/**
* This class allows to acquire information about exceptional
* events. It makes easier to log or display error messages
* and to identify problematic code locations.
*
* Although this class is basically thread-safe it is not intended
* to be shared between threads.
*/
class InotifyException
{
public:
114,6 → 203,10
/**
* It holds all information about inotify event and provides
* access to its particular values.
*
* This class is not (and is not intended to be) thread-safe
* and therefore it must not be used concurrently in multiple
* threads.
*/
class InotifyEvent
{
271,6 → 364,12
 
 
/// inotify watch class
/**
* It holds information about the inotify watch on a particular
* inode.
*
* If the INOTIFY_THREAD_SAFE is defined this class is thread-safe.
*/
class InotifyWatch
{
public:
289,11 → 388,14
m_wd((int32_t) -1),
m_fEnabled(fEnabled)
{
IN_LOCK_INIT
}
/// Destructor.
~InotifyWatch() {}
~InotifyWatch()
{
IN_LOCK_DONE
}
/// Returns the watch descriptor.
/**
324,7 → 426,7
/// Sets the watch event mask.
/**
* If the watch is active (added to an instance of Inofify)
* If the watch is active (added to an instance of Inotify)
* this method may fail due to unsuccessful re-setting
* the watch in the kernel.
*
345,7 → 447,7
/// Enables/disables the watch.
/**
* If the watch is active (added to an instance of Inofify)
* If the watch is active (added to an instance of Inotify)
* this method may fail due to unsuccessful re-setting
* the watch in the kernel.
*
374,6 → 476,8
int32_t m_wd; ///< watch descriptor
Inotify* m_pInotify; ///< inotify object
bool m_fEnabled; ///< events enabled yes/no
IN_LOCK_DECL
};
 
 
385,6 → 489,12
 
 
/// inotify class
/**
* It holds information about the inotify device descriptor
* and manages the event queue.
*
* If the INOTIFY_THREAD_SAFE is defined this class is thread-safe.
*/
class Inotify
{
public:
457,12 → 567,31
* enabled or not).
*
* \return count of watches
*
* \sa GetEnabledCount()
*/
inline size_t GetWatchCount() const
{
return (size_t) m_paths.size();
IN_READ_BEGIN
size_t n = (size_t) m_paths.size();
IN_READ_END
return n;
}
/// Returns the count of enabled watches.
/**
* \return count of enabled watches
*
* \sa GetWatchCount()
*/
inline size_t GetEnabledCount() const
{
IN_READ_BEGIN
size_t n = (size_t) m_watches.size();
IN_READ_END
return n;
}
/// Waits for inotify events.
/**
* It waits until one or more events occur. When called
484,7 → 613,13
*
* \return count of events
*/
int GetEventCount();
inline size_t GetEventCount()
{
IN_READ_BEGIN
size_t n = (size_t) m_events.size();
IN_READ_END
return n;
}
/// Extracts a queued inotify event.
/**
591,8 → 726,11
unsigned char m_buf[INOTIFY_BUFLEN]; ///< buffer for events
std::deque<InotifyEvent> m_events; ///< event queue
IN_LOCK_DECL
friend class InotifyWatch;
};
 
 
#endif //_INOTIFYCXX_H_
 
/incron/trunk/usertable.h
171,6 → 171,13
*/
bool PrepareArgs(const std::string& rCmd, int& argc, char**& argv);
/// Frees memory allocated for arguments.
/**
* \param[in] argc argument count
* \param[in] argv argument array
*/
void CleanupArgs(int argc, char** argv);
};
 
#endif //_USERTABLE_H_