Subversion Repositories public

Compare Revisions

Ignore whitespace Rev 69 → Rev 68

/incron/trunk/incroncfg.cpp
File deleted
/incron/trunk/appinst.cpp
File deleted
/incron/trunk/appargs.cpp
File deleted
/incron/trunk/appinst.h
File deleted
/incron/trunk/appargs.h
File deleted
/incron/trunk/incroncfg.h
File deleted
/incron/trunk/incron.conf.example
File deleted
/incron/trunk/incrontab.5
1,10 → 1,10
.TH "incrontab" "5" "0.5.0" "Lukas Jelinek" "incron documentation"
.TH "incrontab" "5" "0.4.0" "Lukas Jelinek" "incron documentation"
.SH "NAME"
incrontab \- tables for driving inotify cron (incron)
.SH "DESCRIPTION"
An incrontab file contains instructions to the \fIincrond\fR(8) daemon of the general form: "run this command on these file events". There are two categories of tables: system tables (with root privileges) and user tables (with user privileges).
 
System tables are (by default) located in /etc/incron.d and may have any names. Each system table exists separately inside incron and their watches never collide.
System tables are located in /etc/incron.d and may have any names. Each system table exists separately inside incron and their watches never collide.
 
Each user has their own table, and commands in any given incrontab will be executed as the user who owns the incrontab. System users (such as apache, postfix, nobody etc.) may have their own incrontab.
 
/incron/trunk/ict-main.cpp
22,73 → 22,117
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/inotify.h>
#include <fcntl.h>
 
#include "inotify-cxx.h"
#include "appargs.h"
 
#include "incron.h"
#include "incrontab.h"
#include "incroncfg.h"
 
 
/// Alternative editor
// Alternative editor
#define INCRON_ALT_EDITOR "/etc/alternatives/editor"
 
/// Default (hard-wired) editor
// #define INCRON_DEFAULT_EDITOR "nano" // for vim haters like me ;-)
#define INCRON_DEFAULT_EDITOR "vim"
 
/// incrontab version string
#define INCRONTAB_VERSION INCRONTAB_NAME " " INCRON_VERSION
 
/// incrontab description string
#define INCRONTAB_DESCRIPTION "incrontab - inotify cron table manipulator\n" \
"(c) Lukas Jelinek, 2006, 2007"
const char* argp_program_version = INCRON_TAB_NAME " " INCRON_VERSION;
const char* argp_program_bug_address = INCRON_BUG_ADDRESS;
 
/// incrontab help string
#define INCRONTAB_HELP INCRONTAB_DESCRIPTION "\n\n" \
"usage: incrontab [<options>] <operation>\n" \
" incrontab [<options>] <FILE-TO-IMPORT>\n\n" \
"<operation> may be one of the following:\n" \
" -?, --about gives short information about program\n" \
" -h, --help prints this help text\n" \
" -l, --list lists user table\n" \
" -r, --remove removes user table\n" \
" -e, --edit provides editting user table\n" \
" -t, --types list supported event types\n" \
" -d, --reload request incrond to reload user table\n" \
" -V, --version prints program version\n\n" \
"\n" \
"These options may be used:\n" \
" -u <USER>, --user=<USER> overrides current user (requires root privileges)\n" \
" -f <FILE>, --config=<FILE> overrides default configuration file (requires root privileges)\n\n" \
"For reporting bugs please use http:://bts.aiken.cz\n"
static char doc[] = "incrontab - incron table manipulator\n(c) Lukas Jelinek, 2006, 2007";
 
static char args_doc[] = "FILE";
 
static struct argp_option options[] = {
{"list", 'l', 0, 0, "List the current table" },
{"remove", 'r', 0, 0, "Remove the table completely" },
{"edit", 'e', 0, 0, "Edit the table" },
{"types", 't', 0, 0, "List all supported event types" },
{"user", 'u', "USER", 0, "Override the current user" },
{ 0 }
};
 
/// incrontab operations
typedef enum
{
OPER_NONE, /// nothing
OPER_LIST, /// list table
OPER_REMOVE, /// remove table
OPER_EDIT, /// edit table
OPER_TYPES /// list event types
} InCronTab_Operation_t;
 
/// incrontab arguments
struct arguments
{
char *user; /// user name
int oper; /// operation code
char *file; /// file to import
};
 
/// Parses the program options (arguments).
/**
* \param[in] key argument key (name)
* \param[in] arg argument value
* \param[out] state options setting
* \return 0 on success, ARGP_ERR_UNKNOWN on unknown argument(s)
*/
static error_t parse_opt(int key, char *arg, struct argp_state *state)
{
struct arguments* arguments = (struct arguments*) state->input;
switch (key) {
case 'l':
arguments->oper = OPER_LIST;
break;
case 'r':
arguments->oper = OPER_REMOVE;
break;
case 'e':
arguments->oper = OPER_EDIT;
break;
case 't':
arguments->oper = OPER_TYPES;
break;
case 'u':
arguments->user = arg;
break;
case ARGP_KEY_ARG:
if (state->arg_num >= 1)
argp_usage(state);
arguments->file = arg;
break;
case ARGP_KEY_END:
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
 
/// Program arguments
static struct argp argp = { options, parse_opt, args_doc, doc };
 
 
 
/// Copies a file to an user table.
/**
* \param[in] rPath path to file
* \param[in] rUser user name
* \param[in] path path to file
* \param[in] user user name
* \return true = success, false = failure
*/
bool copy_from_file(const std::string& rPath, const std::string& rUser)
bool copy_from_file(const char* path, const char* user)
{
fprintf(stderr, "copying table from file '%s'\n", rPath.c_str());
IncronTab tab;
std::string s(rPath);
InCronTab tab;
std::string s(path);
if (s == "-")
s = "/dev/stdin";
if (!tab.Load(s)) {
fprintf(stderr, "cannot load table from file '%s'\n", rPath.c_str());
fprintf(stderr, "cannot load table from file: %s\n", path);
return false;
}
std::string out(IncronTab::GetUserTablePath(rUser));
std::string out(InCronTab::GetUserTablePath(user));
if (!tab.Save(out)) {
fprintf(stderr, "cannot create table for user '%s'\n", rUser.c_str());
fprintf(stderr, "cannot create table for user: %s\n", user);
return false;
}
97,17 → 141,15
 
/// Removes an user table.
/**
* \param[in] rUser user name
* \param[in] user user name
* \return true = success, false = failure
*/
bool remove_table(const std::string& rUser)
bool remove_table(const char* user)
{
fprintf(stderr, "removing table for user '%s'\n", rUser.c_str());
std::string tp(IncronTab::GetUserTablePath(rUser));
std::string tp(InCronTab::GetUserTablePath(user));
if (unlink(tp.c_str()) != 0 && errno != ENOENT) {
fprintf(stderr, "cannot remove table for user '%s': %s\n", rUser.c_str(), strerror(errno));
fprintf(stderr, "cannot remove table for user: %s\n", user);
return false;
}
116,24 → 158,27
 
/// Lists an user table.
/**
* \param[in] rUser user name
* \param[in] user user name
* \return true = success, false = failure
*/
bool list_table(const std::string& rUser)
bool list_table(const char* user)
{
std::string tp(IncronTab::GetUserTablePath(rUser));
std::string tp(InCronTab::GetUserTablePath(user));
FILE* f = fopen(tp.c_str(), "r");
if (f == NULL) {
if (euidaccess(tp.c_str(), R_OK) != 0) {
if (errno == ENOENT) {
fprintf(stderr, "no table for %s\n", rUser.c_str());
fprintf(stderr, "no table for %s\n", user);
return true;
}
else {
fprintf(stderr, "cannot read table for '%s': %s\n", rUser.c_str(), strerror(errno));
fprintf(stderr, "cannot read table for %s: %s\n", user, strerror(errno));
return false;
}
}
FILE* f = fopen(tp.c_str(), "r");
if (f == NULL)
return false;
char s[1024];
while (fgets(s, 1024, f) != NULL) {
147,7 → 192,7
 
/// Allows to edit an user table.
/**
* \param[in] rUser user name
* \param[in] user user name
* \return true = success, false = failure
*
* \attention This function is very complex and may contain
154,13 → 199,13
* various bugs including security ones. Please keep
* it in mind..
*/
bool edit_table(const std::string& rUser)
bool edit_table(const char* user)
{
std::string tp(IncronTab::GetUserTablePath(rUser));
std::string tp(InCronTab::GetUserTablePath(user));
struct passwd* ppwd = getpwnam(rUser.c_str());
struct passwd* ppwd = getpwnam(user);
if (ppwd == NULL) {
fprintf(stderr, "cannot find user '%s': %s\n", rUser.c_str(), strerror(errno));
fprintf(stderr, "cannot find user %s: %s\n", user, strerror(errno));
return false;
}
174,7 → 219,7
uid_t ig = getegid();
 
if (setegid(gid) != 0 || seteuid(uid) != 0) {
fprintf(stderr, "cannot change effective UID/GID for user '%s': %s\n", rUser.c_str(), strerror(errno));
fprintf(stderr, "cannot change effective UID/GID for user %s: %s\n", user, strerror(errno));
return false;
}
189,7 → 234,6
FILE* in = NULL;
time_t mt = (time_t) 0;
const char* e = NULL;
std::string ed;
if (setegid(ig) != 0 || seteuid(iu) != 0) {
fprintf(stderr, "cannot change effective UID/GID: %s\n", strerror(errno));
209,13 → 253,13
if (errno == ENOENT) {
in = fopen("/dev/null", "r");
if (in == NULL) {
fprintf(stderr, "cannot get empty table for '%s': %s\n", rUser.c_str(), strerror(errno));
fprintf(stderr, "cannot get empty table for %s: %s\n", user, strerror(errno));
fclose(out);
goto end;
}
}
else {
fprintf(stderr, "cannot read old table for '%s': %s\n", rUser.c_str(), strerror(errno));
fprintf(stderr, "cannot read old table for %s: %s\n", user, strerror(errno));
fclose(out);
goto end;
}
239,27 → 283,16
// Editor selecting algorithm:
// 1. Check EDITOR environment variable
// 2. Check VISUAL environment variable
// 3. Try to get from configuration
// 4. Check presence of /etc/alternatives/editor
// 5. Use hard-wired editor
// 3. Check presence of /etc/alternatives/editor
// 4. Use hard-wired editor
e = getenv("EDITOR");
if (e == NULL) {
e = getenv("VISUAL");
if (e == NULL) {
if (!IncronCfg::GetValue("editor", ed))
throw InotifyException("configuration is corrupted", EINVAL);
if (!ed.empty()) {
e = ed.c_str();
}
else {
if (access(INCRON_ALT_EDITOR, X_OK) == 0)
e = INCRON_ALT_EDITOR;
else
e = INCRON_DEFAULT_EDITOR;
}
if (access(INCRON_ALT_EDITOR, X_OK) == 0)
e = INCRON_ALT_EDITOR;
else
e = INCRON_DEFAULT_EDITOR;
}
}
268,7 → 301,7
pid_t pid = fork();
if (pid == 0) {
if (setgid(gid) != 0 || setuid(uid) != 0) {
fprintf(stderr, "cannot set user '%s': %s\n", rUser.c_str(), strerror(errno));
fprintf(stderr, "cannot set user %s: %s\n", user, strerror(errno));
goto end;
}
304,7 → 337,7
}
{
IncronTab ict;
InCronTab ict;
if (ict.Load(s) && ict.Save(tp)) {
if (chmod(tp.c_str(), S_IRUSR | S_IWUSR) != 0) {
fprintf(stderr, "cannot change mode of temporary file: %s\n", strerror(errno));
350,103 → 383,36
printf("\n");
}
 
/// Reloads an user table.
/**
* \param[in] rUser user name
* \return true = success, false = otherwise
*/
bool reload_table(const std::string& rUser)
{
fprintf(stderr, "requesting table reload for user '%s'...\n", rUser.c_str());
std::string tp(IncronTab::GetUserTablePath(rUser));
int fd = open(tp.c_str(), O_WRONLY | O_APPEND);
if (fd == -1) {
if (errno == ENOENT) {
fprintf(stderr, "no table for '%s'\n", rUser.c_str());
return true;
}
else {
fprintf(stderr, "cannot access table for '%s': %s\n", rUser.c_str(), strerror(errno));
return false;
}
}
close(fd);
fprintf(stderr, "request done\n");
return true;
}
 
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("list", 'l', AAT_NO_VALUE, false)
&& AppArgs::AddOption("remove", 'r', AAT_NO_VALUE, false)
&& AppArgs::AddOption("edit", 'e', AAT_NO_VALUE, false)
&& AppArgs::AddOption("types", 't', AAT_NO_VALUE, false)
&& AppArgs::AddOption("reload", 'd', AAT_NO_VALUE, false)
&& AppArgs::AddOption("user", 'u', AAT_MANDATORY_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;
}
struct arguments arguments;
AppArgs::Parse(argc, argv);
arguments.user = NULL;
arguments.oper = OPER_NONE;
arguments.file = NULL;
if (AppArgs::ExistsOption("help")) {
fprintf(stderr, "%s\n", INCRONTAB_HELP);
return 0;
}
argp_parse (&argp, argc, argv, 0, 0, &arguments);
if (AppArgs::ExistsOption("about")) {
fprintf(stderr, "%s\n", INCRONTAB_DESCRIPTION);
return 0;
}
if (AppArgs::ExistsOption("version")) {
fprintf(stderr, "%s\n", INCRONTAB_VERSION);
return 0;
}
bool oper = AppArgs::ExistsOption("list")
|| AppArgs::ExistsOption("remove")
|| AppArgs::ExistsOption("edit")
|| AppArgs::ExistsOption("types")
|| AppArgs::ExistsOption("reload");
 
size_t vals = AppArgs::GetValueCount();
if (!oper && vals == 0) {
fprintf(stderr, "invalid arguments - specify operation or source file\n");
if (arguments.file != NULL && arguments.oper != OPER_NONE) {
fprintf(stderr, "invalid arguments - specify source file or operation\n");
return 1;
}
if (oper && vals > 0) {
fprintf(stderr, "invalid arguments - operation and source file cannot be combined\n");
if (arguments.file == NULL && arguments.oper == OPER_NONE) {
fprintf(stderr, "invalid arguments - specify source file or operation\n");
return 1;
}
uid_t uid = getuid();
std::string user;
bool chuser = AppArgs::GetOption("user", user);
if (uid != 0 && chuser) {
fprintf(stderr, "cannot override user to '%s': insufficient privileges\n", user.c_str());
if (uid != 0 && arguments.user != NULL) {
fprintf(stderr, "cannot access table for user %s: permission denied\n", arguments.user);
return 1;
}
struct passwd pwd;
if (!chuser) {
if (arguments.user == NULL) {
struct passwd* ppwd = getpwuid(uid);
if (ppwd == NULL) {
fprintf(stderr, "cannot determine current user\n");
453,75 → 419,44
return 1;
}
memcpy(&pwd, ppwd, sizeof(pwd));
user = pwd.pw_name;
arguments.user = pwd.pw_name;
}
else if (getpwnam(user.c_str()) == NULL) {
fprintf(stderr, "user '%s' not found\n", user.c_str());
else if (getpwnam(arguments.user) == NULL) {
fprintf(stderr, "user %s not found\n", arguments.user);
return 1;
}
try {
if (!InCronTab::CheckUser(arguments.user)) {
fprintf(stderr, "user %s is not allowed to use incron\n", arguments.user);
return 1;
}
IncronCfg::Init();
std::string cfg(INCRON_CONFIG);
if (AppArgs::GetOption("config", cfg)) {
if (uid != 0) {
fprintf(stderr, "insufficient privileges to use custom configuration (will use default)\n");
}
else if (euidaccess(cfg.c_str(), R_OK) != 0) {
perror("cannot read configuration file (will use default)");
}
}
IncronCfg::Load(cfg);
if (!IncronTab::CheckUser(user)) {
fprintf(stderr, "user '%s' is not allowed to use incron\n", user.c_str());
return 1;
}
if (!oper) {
std::string file;
if (!AppArgs::GetValue(0, file)
|| !copy_from_file(file, user))
{
switch (arguments.oper) {
case OPER_NONE:
fprintf(stderr, "copying table from file: %s\n", arguments.file);
if (!copy_from_file(arguments.file, arguments.user))
return 1;
}
}
else {
if (AppArgs::ExistsOption("list")) {
if (!list_table(user))
return 1;
}
else if (AppArgs::ExistsOption("remove")) {
if (!remove_table(user))
return 1;
}
else if (AppArgs::ExistsOption("edit")) {
if (!edit_table(user))
return 1;
}
else if (AppArgs::ExistsOption("types")) {
list_types();
}
else if (AppArgs::ExistsOption("reload")) {
if (!reload_table(user))
return 1;
}
else {
fprintf(stderr, "invalid usage\n");
break;
case OPER_LIST:
if (!list_table(arguments.user))
return 1;
}
}
return 0;
} catch (InotifyException e) {
fprintf(stderr, "*** unhandled exception occurred ***\n");
fprintf(stderr, "%s\n", e.GetMessage().c_str());
fprintf(stderr, "error: (%i) %s\n", e.GetErrorNumber(), strerror(e.GetErrorNumber()));
return 1;
break;
case OPER_REMOVE:
fprintf(stderr, "removing table for user %s\n", arguments.user);
if (!remove_table(arguments.user))
return 1;
break;
case OPER_EDIT:
if (!edit_table(arguments.user))
return 1;
break;
case OPER_TYPES:
list_types();
break;
default:
fprintf(stderr, "invalid usage\n");
return 1;
}
return 0;
}
/incron/trunk/CHANGELOG
1,14 → 1,3
0.5.0
* based on inotify-cxx 0.7.1
* bug related to events names fixed (#0000134)
* instance locking (only one instance allowed - also fixes #0000136)
* configuration introduced (including custom cfg. files)
* waiting for child processes rewritten to avoid race conditions
* user can request reloading the table (#0000130)
* new implementation for command line arguments
* some code refactoring
 
 
0.4.0 2007-01-13
* based on inotify-cxx 0.7.0
* incrontab has a feature to find out supported event types (--types or -t)
/incron/trunk/README
40,12 → 40,7
 
The binaries must be of course installed as root.
 
If you want to use (after editting) the example configuration
file simply rename it from /etc/incron.conf.example to
/etc/incron.conf (you can also use -f <config> for one-time
use of a custom configuration file).
 
 
4. How to use
The incron daemon (incrond) must be run under root (typically from
runlevel script etc.). It loads the current user tables and hooks
93,10 → 88,7
/etc/incron.d and their commands use root privileges. System tables
are intended to be changed directly (without incrontab).
 
Some parameters of both incrontab and incrond can be changed by
the configuration. See the example file for more information.
 
 
5. Bugs, suggestions
THIS PROGRAM IS AN ALPHA VERSION. IT PROBABLY CONTAINS BUGS AND
THEREFORE IT IS NOT INTENDED FOR PRODUCTION USE.
/incron/trunk/incrontab.cpp
21,15 → 21,13
#include "inotify-cxx.h"
 
#include "incrontab.h"
#include "incroncfg.h"
 
/*
 
/// Allowed users
#define INCRON_ALLOW_PATH "/etc/incron.allow"
 
/// Denied users
#define INCRON_DENY_PATH "/etc/incron.deny"
*/
 
/*
* ALLOW/DENY SEMANTICS
46,7 → 44,7
 
 
 
IncronTabEntry::IncronTabEntry()
InCronTabEntry::InCronTabEntry()
: m_uMask(0),
m_fNoLoop(false)
{
53,7 → 51,7
}
 
IncronTabEntry::IncronTabEntry(const std::string& rPath, uint32_t uMask, const std::string& rCmd)
InCronTabEntry::InCronTabEntry(const std::string& rPath, uint32_t uMask, const std::string& rCmd)
: m_path(rPath),
m_uMask(uMask),
m_cmd(rCmd)
61,7 → 59,7
}
 
std::string IncronTabEntry::ToString() const
std::string InCronTabEntry::ToString() const
{
std::ostringstream ss;
80,7 → 78,7
return ss.str();
}
 
bool IncronTabEntry::Parse(const std::string& rStr, IncronTabEntry& rEntry)
bool InCronTabEntry::Parse(const std::string& rStr, InCronTabEntry& rEntry)
{
unsigned long u;
std::string s1, s2, s3;
125,7 → 123,7
return true;
}
 
std::string IncronTabEntry::GetSafePath(const std::string& rPath)
std::string InCronTabEntry::GetSafePath(const std::string& rPath)
{
std::ostringstream stream;
145,7 → 143,7
return stream.str();
}
 
bool IncronTab::Load(const std::string& rPath)
bool InCronTab::Load(const std::string& rPath)
{
m_tab.clear();
154,9 → 152,9
return false;
char s[1000];
IncronTabEntry e;
InCronTabEntry e;
while (fgets(s, 1000, f) != NULL) {
if (IncronTabEntry::Parse(s, e)) {
if (InCronTabEntry::Parse(s, e)) {
m_tab.push_back(e);
}
}
166,13 → 164,13
return true;
}
 
bool IncronTab::Save(const std::string& rPath)
bool InCronTab::Save(const std::string& rPath)
{
FILE* f = fopen(rPath.c_str(), "w");
if (f == NULL)
return false;
std::deque<IncronTabEntry>::iterator it = m_tab.begin();
std::deque<InCronTabEntry>::iterator it = m_tab.begin();
while (it != m_tab.end()) {
fputs((*it).ToString().c_str(), f);
fputs("\n", f);
184,21 → 182,14
return true;
}
 
bool IncronTab::CheckUser(const std::string& rUser)
bool InCronTab::CheckUser(const std::string& rUser)
{
char s[100], u[100];
std::string path;
if (!IncronCfg::GetValue("allowed_users", path))
throw InotifyException("configuration is corrupted", EINVAL);
FILE* f = fopen(path.c_str(), "r");
FILE* f = fopen(INCRON_ALLOW_PATH, "r");
if (f == NULL) {
if (errno == ENOENT) {
if (!IncronCfg::GetValue("denied_users", path))
throw InotifyException("configuration is corrupted", EINVAL);
f = fopen(path.c_str(), "r");
f = fopen(INCRON_DENY_PATH, "r");
if (f == NULL) {
return errno == ENOENT;
}
230,22 → 221,18
return false;
}
 
std::string IncronTab::GetUserTablePath(const std::string& rUser)
std::string InCronTab::GetUserTablePath(const std::string& rUser)
{
std::string s;
if (!IncronCfg::GetValue("user_table_dir", s))
throw InotifyException("configuration is corrupted", EINVAL);
return IncronCfg::BuildPath(s, rUser);
std::string s(INCRON_USER_TABLE_BASE);
s.append(rUser);
return s;
}
 
std::string IncronTab::GetSystemTablePath(const std::string& rName)
std::string InCronTab::GetSystemTablePath(const std::string& rName)
{
std::string s;
if (!IncronCfg::GetValue("system_table_dir", s))
throw InotifyException("configuration is corrupted", EINVAL);
return IncronCfg::BuildPath(s, rName);
std::string s(INCRON_SYS_TABLE_BASE);
s.append(rName);
return s;
}
 
 
/incron/trunk/incrond.8
1,21 → 1,21
.TH "incrond" "8" "0.5.0" "Lukas Jelinek" "incron documentation"
.TH "incrond" "8" "0.4.0" "Lukas Jelinek" "incron documentation"
.SH "NAME"
incrond \- inotify cron (incron) daemon
 
.SH "SYNOPSIS"
\fBincrond\fR [ <options> ]
\fBincrond\fR [ \fB\-n\fR | \fB\-k\fR ]
.SH "DESCRIPTION"
The inotify cron daemon (\fIincrond\fR) is a daemon which monitors filesystem events and executes commands defined in system and user tables. It's use is generally similar to \fIcron\fR(8).
 
\fIincrond\fR can be started from /etc/rc, /etc/rc.local and so on. It daemonizes itself (returns immediately) and doesn't need to be started with & and through \fInohup\fR(1). It can be run on foreground too.
 
\fIincrond\fR uses two categories of tables \fIincrontab\fR(5). System tables are usually located in /etc/incron.d and are maintained outside of incron (e.g. by various applications). These tables work on root rights level and thus any file may be watched and commands are executed with root privileges.
\fIincrond\fR uses two categories of tables \fIincrontab\fR(5). System tables are located in /etc/incron.d and are maintained outside of incron (e.g. by various applications). These tables work on root rights level and thus any file may be watched and commands are executed with root privileges.
 
User tables are located in /var/spool/incron by default and have names based on user accounts. These tables use users' access rights, thus only files which the user may access are watched. Commands are executed with users' privileges.
User tables are located in /var/spool/incron and have names based on user accounts. These tables use users' access rights, thus only files which the user may access are watched. Commands are executed with users' privileges.
 
If a table (incrontab) is changed \fIincrond\fR reacts immediately and reloads the table. Currently running child processes (commands) are not affected.
 
There are two files determining whether an user is allowed to use incron. These files have very simple syntax \- one user name per line. If /etc/incron.allow exists the user must be noted there to be allowed to use incron. Otherwise if /etc/incron.deny exists the user must not be noted there to use incron. If none of these files exists there is no other restriction whether anybody may use incron. Location of these files can be changed in the configuration.
There are two files determining whether an user is allowed to use incron. These files have very simple syntax \- one user name per line. If /etc/incron.allow exists the user must be noted there to be allowed to use incron. Otherwise if /etc/incron.deny exists the user must not be noted there to use incron. If none of these files exists there is no other restriction whether anybody may use incron.
 
The daemon itself is currently not protected against looping. If a command executed due to an event causes the same event it leads to an infinite loop unless a flag mask containing IN_NO_LOOP is specified. Please beware of this and do not allow permission for use incron to unreliable users.
 
22,9 → 22,7
 
\fB\-n\fR (or \fB\-\-foreground\fR) option causes running on foreground. This is useful especially for testing, debugging and optimization.
 
\fB\-k\fR (or \fB\-\-kill\fR) option terminates a running instance of \fBincrond\fR.
 
\fB\-f <FILE>\fR (or \fB\-\-config=<FILE>\fR) option specifies another location for the configuration file (/etc/incron.conf is used by default).
\fB\-k\fR (or \fB\-\-kill\fR) option terminates all running instances of \fBincrond\fR. It takes effect only on instances of the same executable (using the same file).
.SH "SEE ALSO"
incrontab(1), incrontab(5)
.SH "BUGS"
/incron/trunk/icd-main.cpp
25,13 → 25,10
#include <sys/stat.h>
 
#include "inotify-cxx.h"
#include "appinst.h"
#include "appargs.h"
 
#include "incron.h"
#include "incrontab.h"
#include "usertable.h"
#include "incroncfg.h"
 
 
/// Logging options (console as fallback, log PID)
40,28 → 37,19
/// Logging facility (use CRON)
#define INCRON_LOG_FACIL LOG_CRON
 
/// incrond version string
#define INCROND_VERSION INCROND_NAME " " INCRON_VERSION
/// Help text
#define INCROND_HELP_TEXT "Usage: incrond [option]\n" \
"incrond - inotify cron daemon\n" \
"(c) Lukas Jelinek, 2006, 2007\n\n" \
"-h, --help Give this help list\n" \
"-n, --foreground Run on foreground (don't daemonize)\n" \
"-k, --kill Terminate running instance of incrond\n" \
"-V, --version Print program version\n\n" \
"Report bugs to <bugs@aiken.cz>"
 
/// incrontab description string
#define INCROND_DESCRIPTION "incrond - inotify cron daemon\n" \
"(c) Lukas Jelinek, 2006, 2007"
#define INCROND_VERSION_TEXT INCRON_DAEMON_NAME " " INCRON_VERSION
 
/// incrontab help string
#define INCROND_HELP INCROND_DESCRIPTION "\n\n" \
"usage: incrond [<options>]\n\n" \
"<operation> may be one of the following:\n" \
"These options may be used:\n" \
" -?, --about gives short information about program\n" \
" -h, --help prints this help text\n" \
" -n, --foreground runs on foreground (no daemonizing)\n" \
" -k, --kill terminates running instance of incrond\n" \
" -f <FILE>, --config=<FILE> overrides default configuration file (requires root privileges)\n" \
" -V, --version prints program version\n\n" \
"For reporting bugs please use http:://bts.aiken.cz\n"
 
 
 
/// User name to user table mapping table
SUT_MAP g_ut;
 
106,8 → 94,6
}
 
 
 
 
/// Attempts to load all user incron tables.
/**
* Loaded tables are registered for processing events.
120,11 → 106,7
{
// WARNING - this function has not been optimized!!!
std::string s;
if (!IncronCfg::GetValue("system_table_dir", s))
throw InotifyException("configuration system is corrupted", EINVAL);
DIR* d = opendir(s.c_str());
DIR* d = opendir(INCRON_SYS_TABLE_BASE);
if (d != NULL) {
syslog(LOG_NOTICE, "loading system tables");
131,12 → 113,11
struct dirent* pDe = NULL;
while ((pDe = readdir(d)) != NULL) {
std::string un(pDe->d_name);
std::string path(IncronCfg::BuildPath(s, pDe->d_name));
bool ok = pDe->d_type == DT_REG;
if (pDe->d_type == DT_UNKNOWN) {
struct stat st;
if (stat(path.c_str(), &st) == 0)
if (stat(pDe->d_name, &st) == 0)
ok = S_ISREG(st.st_mode);
}
143,7 → 124,7
if (ok) {
syslog(LOG_INFO, "loading table %s", pDe->d_name);
UserTable* pUt = new UserTable(pEd, un, true);
g_ut.insert(SUT_MAP::value_type(path, pUt));
g_ut.insert(SUT_MAP::value_type(std::string(INCRON_SYS_TABLE_BASE) + un, pUt));
pUt->Load();
}
}
154,10 → 135,8
syslog(LOG_WARNING, "cannot open system table directory (ignoring)");
}
if (!IncronCfg::GetValue("user_table_dir", s))
throw InotifyException("configuration system is corrupted", EINVAL);
d = opendir(s.c_str());
d = opendir(INCRON_USER_TABLE_BASE);
if (d == NULL)
throw InotifyException("cannot open user table directory", errno);
166,12 → 145,11
struct dirent* pDe = NULL;
while ((pDe = readdir(d)) != NULL) {
std::string un(pDe->d_name);
std::string path(IncronCfg::BuildPath(s, pDe->d_name));
bool ok = pDe->d_type == DT_REG;
if (pDe->d_type == DT_UNKNOWN) {
struct stat st;
if (stat(path.c_str(), &st) == 0)
if (stat(pDe->d_name, &st) == 0)
ok = S_ISREG(st.st_mode);
}
179,7 → 157,7
if (UserTable::CheckUser(pDe->d_name)) {
syslog(LOG_INFO, "loading table for user %s", pDe->d_name);
UserTable* pUt = new UserTable(pEd, un, false);
g_ut.insert(SUT_MAP::value_type(path, pUt));
g_ut.insert(SUT_MAP::value_type(std::string(INCRON_USER_TABLE_BASE) + un, pUt));
pUt->Load();
}
else {
241,9 → 219,71
|| strcmp(s, longCmd) == 0;
}
 
/// Attempts to kill all running instances of incrond.
/**
* It kills only instances which use the same executable image
* as the currently running one.
*
* \return true = at least one instance killed, false = otherwise
* \attention More than one instance may be currently run simultaneously.
*/
bool kill_incrond()
{
unsigned pid_self = (unsigned) getpid(); // self PID
char s[PATH_MAX];
snprintf(s, PATH_MAX, "/proc/%u/exe", pid_self);
char path_self[PATH_MAX];
ssize_t len = readlink(s, path_self, PATH_MAX-1);
if (len <= 0)
return false;
path_self[len] = '\0';
DIR* d = opendir("/proc");
if (d == NULL)
return false;
bool ok = false;
char path[PATH_MAX];
struct dirent* de = NULL;
while ((de = readdir(d)) != NULL) {
bool to = false;
if (de->d_type == DT_DIR)
to = true;
else if (de->d_type == DT_UNKNOWN) {
// fallback way
snprintf(s, PATH_MAX, "/proc/%s", de->d_name);
struct stat st;
if (stat(s, &st) == 0 && S_ISDIR(st.st_mode))
to = true;
}
if (to) {
unsigned pid;
if (sscanf(de->d_name, "%u", &pid) == 1 // PID successfully retrieved
&& pid != pid_self) // don't do suicide!
{
snprintf(s, PATH_MAX, "/proc/%u/exe", pid);
len = readlink(s, path, PATH_MAX-1);
if (len > 0) {
path[len] = '\0';
if ( strcmp(path, path_self) == 0
&& kill((pid_t) pid, SIGTERM) == 0)
ok = true;
}
}
}
}
closedir(d);
return ok;
}
 
/// Initializes a poll array.
/**
* \param[out] pfd poll structure array
* \param[in] pipefd pipe file descriptor
* \param[in] infd inotify infrastructure file descriptor
*/
268,113 → 308,48
*/
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");
if (argc > 2) {
fprintf(stderr, "error: too many parameters\n");
fprintf(stderr, "give --help or -h for more information\n");
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;
}
AppInstance app("incrond");
IncronCfg::Init();
if (AppArgs::ExistsOption("kill")) {
fprintf(stderr, "attempting to terminate a running instance of incrond...\n");
if (app.SendSignal(SIGTERM)) {
fprintf(stderr, "instance(s) notified, going down\n");
if (argc == 2) {
if (check_parameter(argv[1], "-h", "--help")) {
printf("%s\n", INCROND_HELP_TEXT);
return 0;
}
else {
fprintf(stderr, "error - incrond probably not running\n");
else if (check_parameter(argv[1], "-n", "--foreground")) {
g_daemon = false;
}
else if (check_parameter(argv[1], "-k", "--kill")) {
fprintf(stderr, "attempting to terminate a running instance of incrond...\n");
if (kill_incrond()) {
fprintf(stderr, "instance(s) notified, going down\n");
return 0;
}
else {
fprintf(stderr, "error - incrond probably not running\n");
return 1;
}
}
else if (check_parameter(argv[1], "-V", "--version")) {
printf("%s\n", INCROND_VERSION_TEXT);
return 0;
}
else {
fprintf(stderr, "error - unrecognized parameter: %s\n", argv[1]);
return 1;
}
}
if (AppArgs::ExistsOption("foreground"))
g_daemon = false;
openlog(INCRON_DAEMON_NAME, INCRON_LOG_OPTS, INCRON_LOG_FACIL);
openlog(INCROND_NAME, INCRON_LOG_OPTS, INCRON_LOG_FACIL);
syslog(LOG_NOTICE, "starting service (version %s, built on %s %s)", INCRON_VERSION, __DATE__, __TIME__);
std::string cfg;
if (!AppArgs::GetOption("config", cfg))
cfg = INCRON_CONFIG;
IncronCfg::Load(cfg);
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)
daemon(0, 0);
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();
383,9 → 358,9
in.SetCloseOnExec(true);
uint32_t wm = IN_CLOSE_WRITE | IN_DELETE | IN_MOVE | IN_DELETE_SELF | IN_UNMOUNT;
InotifyWatch stw(sysBase, wm);
InotifyWatch stw(INCRON_SYS_TABLE_BASE, wm);
in.Add(stw);
InotifyWatch utw(userBase, wm);
InotifyWatch utw(INCRON_USER_TABLE_BASE, wm);
in.Add(utw);
EventDispatcher ed(g_cldPipe[0], &in, &stw, &utw);
395,8 → 370,9
} catch (InotifyException e) {
int err = e.GetErrorNumber();
syslog(LOG_CRIT, "%s: (%i) %s", e.GetMessage().c_str(), err, strerror(err));
ret = 1;
goto error;
syslog(LOG_NOTICE, "stopping service");
closelog();
return 1;
}
signal(SIGTERM, on_signal);
417,7 → 393,75
if (errno != EINTR)
throw InotifyException("polling failed", errno, NULL);
}
/*
while (in.GetEvent(e)) {
if (e.GetWatch() == &stw) {
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(stw.GetPath() + 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, "system table %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, "system table %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)) {
syslog(LOG_INFO, "system table %s created, loading", e.GetName().c_str());
UserTable* pUt = new UserTable(&in, &ed, e.GetName(), true);
g_ut.insert(SUT_MAP::value_type(std::string(INCRON_SYS_TABLE_BASE) + e.GetName(), pUt));
pUt->Load();
}
}
}
else if (e.GetWatch() == &utw) {
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.GetWatch()->GetPath() + 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(), false);
g_ut.insert(SUT_MAP::value_type(std::string(INCRON_USER_TABLE_BASE) + e.GetName(), pUt));
pUt->Load();
}
}
}
}
else {
ed.DispatchEvent(e);
}
}
*/
}
if (g_cldPipe[0] != -1)
429,14 → 473,11
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;
return 0;
}
/incron/trunk/incrontab.h
22,16 → 22,15
 
#include "strtok.h"
 
/*
/// Incron user table base directory
#define INCRON_USER_TABLE_BASE "/var/spool/incron/"
 
/// Incron system table base directory
#define INCRON_SYS_TABLE_BASE "/etc/incron.d/"
*/
 
 
/// Incron table entry class.
class IncronTabEntry
class InCronTabEntry
{
public:
/// Constructor.
40,7 → 39,7
*
* \sa Parse()
*/
IncronTabEntry();
InCronTabEntry();
 
/// Constructor.
/**
50,10 → 49,10
* \param[in] uMask event mask
* \param[in] rCmd command string
*/
IncronTabEntry(const std::string& rPath, uint32_t uMask, const std::string& rCmd);
InCronTabEntry(const std::string& rPath, uint32_t uMask, const std::string& rCmd);
/// Destructor.
~IncronTabEntry() {}
~InCronTabEntry() {}
/// Converts the entry to string representation.
/**
69,7 → 68,7
* \param[out] rEntry parametrized entry
* \return true = success, false = failure
*/
static bool Parse(const std::string& rStr, IncronTabEntry& rEntry);
static bool Parse(const std::string& rStr, InCronTabEntry& rEntry);
/// Returns the watch filesystem path.
/**
131,20 → 130,20
 
 
/// Incron table class.
class IncronTab
class InCronTab
{
public:
/// Constructor.
IncronTab() {}
InCronTab() {}
/// Destructor.
~IncronTab() {}
~InCronTab() {}
/// Add an entry to the table.
/**
* \param[in] rEntry table entry
*/
inline void Add(const IncronTabEntry& rEntry)
inline void Add(const InCronTabEntry& rEntry)
{
m_tab.push_back(rEntry);
}
181,7 → 180,7
* pass an invalid value the program may crash
* and/or behave unpredictible way!
*/
inline IncronTabEntry& GetEntry(int index)
inline InCronTabEntry& GetEntry(int index)
{
return m_tab[index];
}
226,7 → 225,7
static std::string GetSystemTablePath(const std::string& rName);
 
protected:
std::deque<IncronTabEntry> m_tab; ///< incron table
std::deque<InCronTabEntry> m_tab; ///< incron table
};
 
 
/incron/trunk/inotify-cxx.h
246,11 → 246,8
if (pEvt != NULL) {
m_uMask = (uint32_t) pEvt->mask;
m_uCookie = (uint32_t) pEvt->cookie;
if (pEvt->name != NULL) {
m_name = pEvt->len > 0
? pEvt->name
: "";
}
if (pEvt->name != NULL)
m_name = pEvt->name;
m_pWatch = pWatch;
}
else {
/incron/trunk/usertable.cpp
23,7 → 23,6
#include <sys/stat.h>
 
#include "usertable.h"
#include "incroncfg.h"
 
#ifdef IN_DONT_FOLLOW
#define DONT_FOLLOW(mask) InotifyEvent::IsType(mask, IN_DONT_FOLLOW)
32,7 → 31,7
#endif // IN_DONT_FOLLOW
 
 
PROC_MAP UserTable::s_procMap;
PROC_LIST UserTable::s_procList;
 
extern volatile bool g_fFinish;
extern SUT_MAP g_ut;
165,7 → 164,7
g_fFinish = true;
}
else if (!e.GetName().empty()) {
SUT_MAP::iterator it = g_ut.find(IncronCfg::BuildPath(m_pSys->GetPath(), e.GetName()));
SUT_MAP::iterator it = g_ut.find(m_pSys->GetPath() + e.GetName());
if (it != g_ut.end()) {
UserTable* pUt = (*it).second;
if (e.IsType(IN_CLOSE_WRITE) || e.IsType(IN_MOVED_TO)) {
182,7 → 181,7
else if (e.IsType(IN_CLOSE_WRITE) || e.IsType(IN_MOVED_TO)) {
syslog(LOG_INFO, "system table %s created, loading", e.GetName().c_str());
UserTable* pUt = new UserTable(this, e.GetName(), true);
g_ut.insert(SUT_MAP::value_type(IncronTab::GetSystemTablePath(e.GetName()), pUt));
g_ut.insert(SUT_MAP::value_type(std::string(INCRON_SYS_TABLE_BASE) + e.GetName(), pUt));
pUt->Load();
}
}
193,7 → 192,7
g_fFinish = true;
}
else if (!e.GetName().empty()) {
SUT_MAP::iterator it = g_ut.find(IncronCfg::BuildPath(m_pUser->GetPath(), e.GetName()));
SUT_MAP::iterator it = g_ut.find(m_pUser->GetPath() + e.GetName());
if (it != g_ut.end()) {
UserTable* pUt = (*it).second;
if (e.IsType(IN_CLOSE_WRITE) || e.IsType(IN_MOVED_TO)) {
211,7 → 210,7
if (UserTable::CheckUser(e.GetName().c_str())) {
syslog(LOG_INFO, "table for user %s created, loading", e.GetName().c_str());
UserTable* pUt = new UserTable(this, e.GetName(), false);
g_ut.insert(SUT_MAP::value_type(IncronTab::GetUserTablePath(e.GetName()), pUt));
g_ut.insert(SUT_MAP::value_type(std::string(INCRON_USER_TABLE_BASE) + e.GetName(), pUt));
pUt->Load();
}
}
241,12 → 240,12
void UserTable::Load()
{
m_tab.Load(m_fSysTable
? IncronTab::GetSystemTablePath(m_user)
: IncronTab::GetUserTablePath(m_user));
? InCronTab::GetSystemTablePath(m_user)
: InCronTab::GetUserTablePath(m_user));
int cnt = m_tab.GetCount();
for (int i=0; i<cnt; i++) {
IncronTabEntry& rE = m_tab.GetEntry(i);
InCronTabEntry& rE = m_tab.GetEntry(i);
InotifyWatch* pW = new InotifyWatch(rE.GetPath(), rE.GetMask());
// warning only - permissions may change later
276,19 → 275,6
while (it != m_map.end()) {
InotifyWatch* pW = (*it).first;
m_in.Remove(pW);
PROC_MAP::iterator it2 = s_procMap.begin();
while (it2 != s_procMap.end()) {
if ((*it2).second.pWatch == pW) {
PROC_MAP::iterator it3 = it2;
it2++;
s_procMap.erase(it3);
}
else {
it2++;
}
}
delete pW;
it++;
}
299,7 → 285,7
void UserTable::OnEvent(InotifyEvent& rEvt)
{
InotifyWatch* pW = rEvt.GetWatch();
IncronTabEntry* pE = FindEntry(pW);
InCronTabEntry* pE = FindEntry(pW);
// no entry found - this shouldn't occur
if (pE == NULL)
371,8 → 357,9
if (pE->IsNoLoop())
pW->SetEnabled(false);
pid_t pid = fork();
if (pid == 0) {
ProcData_t pd;
pd.pid = fork();
if (pd.pid == 0) {
// for system table
if (m_fSysTable) {
395,8 → 382,7
}
}
}
else if (pid > 0) {
ProcData_t pd;
else if (pd.pid > 0) {
if (pE->IsNoLoop()) {
pd.onDone = on_proc_done;
pd.pWatch = pW;
406,7 → 392,7
pd.pWatch = NULL;
}
s_procMap.insert(PROC_MAP::value_type(pid, pd));
s_procList.push_back(pd);
}
else {
if (pE->IsNoLoop())
418,7 → 404,7
CleanupArgs(argc, argv);
}
 
IncronTabEntry* UserTable::FindEntry(InotifyWatch* pWatch)
InCronTabEntry* UserTable::FindEntry(InotifyWatch* pWatch)
{
IWCE_MAP::iterator it = m_map.find(pWatch);
if (it == m_map.end())
466,16 → 452,19
 
void UserTable::FinishDone()
{
pid_t res = 0;
int status = 0;
while ((res = waitpid(-1, &status, WNOHANG)) > 0) {
PROC_MAP::iterator it = s_procMap.find(res);
if (it != s_procMap.end()) {
ProcData_t pd = (*it).second;
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);
s_procMap.erase(it);
it = s_procList.erase(it);
}
else {
it++;
}
}
}
 
/incron/trunk/usertable.h
35,6 → 35,7
/// Child process data
typedef struct
{
pid_t pid; ///< PID
proc_done_cb onDone; ///< function called after process finishes
InotifyWatch* pWatch; ///< related watch
} ProcData_t;
43,10 → 44,10
typedef std::map<int, UserTable*> FDUT_MAP;
 
/// Watch-to-tableentry mapping
typedef std::map<InotifyWatch*, IncronTabEntry*> IWCE_MAP;
typedef std::map<InotifyWatch*, InCronTabEntry*> IWCE_MAP;
 
/// Child process list
typedef std::map<pid_t, ProcData_t> PROC_MAP;
typedef std::deque<ProcData_t> PROC_LIST;
 
/// Event dispatcher class.
/**
212,7 → 213,7
if (pw == NULL)
return false;
return IncronTab::CheckUser(user);
return InCronTab::CheckUser(user);
}
private:
220,10 → 221,10
EventDispatcher* m_pEd; ///< event dispatcher
std::string m_user; ///< user name
bool m_fSysTable; ///< system table yes/no
IncronTab m_tab; ///< incron table
InCronTab m_tab; ///< incron table
IWCE_MAP m_map; ///< watch-to-entry mapping
 
static PROC_MAP s_procMap; ///< child process mapping
static PROC_LIST s_procList; ///< child process list
/// Finds an entry for a watch.
/**
230,7 → 231,7
* \param[in] pWatch inotify watch
* \return pointer to the appropriate entry; NULL if no such entry exists
*/
IncronTabEntry* FindEntry(InotifyWatch* pWatch);
InCronTabEntry* FindEntry(InotifyWatch* pWatch);
/// Prepares arguments for creating a child process.
/**
/incron/trunk/Makefile
2,7 → 2,6
PREFIX = /usr/local
USERDATADIR = /var/spool/incron
SYSDATADIR = /etc/incron.d
CFGDIR = /etc
MANPATH = /usr/share/man
RELEASE = incron-`cat VERSION`
RELEASEDIR = /tmp/$(RELEASE)
17,13 → 16,14
WARNINGS = -Wall
CXXAUX = -pipe
 
CPPFLAGS =
CXXFLAGS = $(OPTIMIZE) $(DEBUG) $(WARNINGS) $(CXXAUX)
LDFLAGS = $(WARNINGS)
 
PROGRAMS = incrond incrontab
 
INCROND_OBJ = icd-main.o incrontab.o inotify-cxx.o usertable.o strtok.o appinst.o incroncfg.o appargs.o
INCRONTAB_OBJ = ict-main.o incrontab.o inotify-cxx.o strtok.o incroncfg.o appargs.o
INCROND_OBJ = icd-main.o incrontab.o inotify-cxx.o usertable.o strtok.o
INCRONTAB_OBJ = ict-main.o incrontab.o inotify-cxx.o strtok.o
 
 
all: $(PROGRAMS)
49,7 → 49,6
$(INSTALL) -m 0755 incrond $(PREFIX)/sbin/
$(INSTALL) -m 0755 -o $(USER) -d $(USERDATADIR)
$(INSTALL) -m 0755 -o $(USER) -d $(SYSDATADIR)
$(INSTALL) -m 0644 -o $(USER) incron.conf.example $(CFGDIR)
 
install-man: incrontab.1 incrontab.5 incrond.8
$(INSTALL) -m 0755 -d $(MANPATH)/man1
63,7 → 62,6
[ -d $(PREFIX) ]
rm -f $(PREFIX)/bin/incrontab
rm -f $(PREFIX)/sbin/incrond
rm -f $(CFGDIR)/incron.conf.example
 
uninstall-man:
rm -f $(MANPATH)/man1/incrontab.1
78,7 → 76,6
cp -r doc $(RELEASEDIR)
cp *.h $(RELEASEDIR)
cp *.cpp $(RELEASEDIR)
cp incron.conf.example $(RELEASEDIR)
cp Makefile CHANGELOG COPYING LICENSE-GPL LICENSE-LGPL LICENSE-X11 README TODO VERSION $(RELEASEDIR)
cp incrond.8 incrontab.1 incrontab.5 $(RELEASEDIR)
tar -c -f $(RELEASE).tar -C $(RELEASEDIR)/.. $(RELEASE)
106,12 → 103,10
 
.POSIX:
 
icd-main.o: icd-main.cpp inotify-cxx.h incrontab.h usertable.h incron.h appinst.h incroncfg.h appargs.h
icd-main.o: icd-main.cpp inotify-cxx.h incrontab.h usertable.h incron.h
incrontab.o: incrontab.cpp incrontab.h inotify-cxx.h strtok.h
inotify-cxx.o: inotify-cxx.cpp inotify-cxx.h
usertable.o: usertable.cpp usertable.h strtok.h
ict-main.o: ict-main.cpp incrontab.h incron.h incroncfg.h appargs.h
ict-main.o: ict-main.cpp incrontab.h incron.h
strtok.o: strtok.cpp strtok.h
appinst.o: appinst.cpp appinst.h
incroncfg.o: incroncfg.cpp incroncfg.h
appargs.o: appargs.cpp appargs.h
 
/incron/trunk/incrontab.1
1,10 → 1,10
.TH "incrontab" "1" "0.5.0" "Lukas Jelinek" "incron documentation"
.TH "incrontab" "1" "0.4.0" "Lukas Jelinek" "incron documentation"
.SH "NAME"
incrontab \- table manipulator for inotify cron (incron)
.SH "SYNOPSIS"
\fBincrontab\fR [\fB\-u\fR \fIuser\fR] [\fB\-f\fR \fIconfig\fR] \fIfile\fR
\fBincrontab\fR [\fB\-u\fR \fIuser\fR] \fIfile\fR
 
\fBincrontab\fR [\fB\-u\fR \fIuser\fR] [\fB\-f\fR \fIconfig\fR] [\fB\-l\fR | \fB\-r\fR | \fB\-e\fR | \fB\-t\fR | \fB\-d\fR]
\fBincrontab\fR [\fB\-u\fR \fIuser\fR] [\fB\-l\fR | \fB\-r\fR | \fB\-e\fR | \fB\-t\fR]
.SH "DESCRIPTION"
incrontab is a table manipulator for the inotify cron (incron) system. It creates, removes, modifies and lists user tables (\fIincrontab\fR(5)).
 
12,7 → 12,7
 
All informational messages of this program are printed to the standard error output (stderr).
 
If \fI/etc/incron.allow\fR exists only users listed here may use incron. Otherwise if \fI/etc/incron.deny\fR exists only users NOT listed here may use incron. If none of these files exists everyone is allowed to use incron. (\fBImportant note:\fR This behavior is insecure and will be probably changed to be compatible with the style used by ISC Cron.) Location of this files can be changed in the configuration.
If \fI/etc/incron.allow\fR exists only users listed here may use incron. Otherwise if \fI/etc/incron.deny\fR exists only users NOT listed here may use incron. If none of these files exists everyone is allowed to use incron. (\fBImportant note:\fR This behavior is insecure and will be probably changed to be compatible with the style used by ISC Cron.)
 
The first form of this command imports a file, validates it and stores to the table. "\-" can be used for loading from the standard input.
 
24,25 → 24,9
 
\fB\-r\fR (or \fB\-\-remove\fR) option causes the current table (if any) is permanently remove without any warning or confirmation. Use with caution!
 
\fB\-e\fR (or \fB\-\-edit\fR) option causes executing an editor for editting the user table (see below for the information about editor selection). You can edit your incron table now. If the table is changed it stores the modified version.
\fB\-e\fR (or \fB\-\-edit\fR) option causes executing the editor specified by the EDITOR environment variable (if not defined the hard\-coded editor is executed instead). You can edit your incron table now. If the table is changed it stores the modified version. It's not recommended to use graphical editors (such as gVim, KEdit etc.) due to possible problems with connecting to the X server.
 
\fB\-t\fR (or \fB\-\-types\fR) option causes the list of supported event types (delimited by commas) is printed to the standard output. This feature is intended for front\-end applications to find out which event types was compiled in.
 
\fB\-f <FILE>\fR (or \fB\-\-config=<FILE>\fR) option specifies another location for the configuration file (/etc/incron.conf is used by default). This feature requires root privileges.
 
There is a few complex algorithm how to determine which editor will be user for editting. If any of the following rule succeeds the appropriate editor is used:
 
1. EDITOR environment variable
 
2. VISUAL environment variable
 
3. configuration value
 
4. etc/alternatives/editor
 
5. hard\-wired editor (\fIvim\fR by default)
 
It's not recommended to use graphical editors (such as gVim, KEdit etc.) due to possible problems with connecting to the X server.
.SH "SEE ALSO"
incrond(8), incrontab(5)
.SH "AUTHOR"
/incron/trunk/incron.h
21,19 → 21,16
#define INCRON_NAME "incron"
 
/// Daemon name
#define INCROND_NAME "incrond"
#define INCRON_DAEMON_NAME "incrond"
 
/// Table manipulator name
#define INCRONTAB_NAME "incrontab"
#define INCRON_TAB_NAME "incrontab"
 
/// Application version (release)
#define INCRON_VERSION "0.5.0"
#define INCRON_VERSION "0.4.0"
 
/// Address for sending bugs
#define INCRON_BUG_ADDRESS "<bugs@aiken.cz>"
 
/// Default configuration file
#define INCRON_CONFIG "/etc/incron.conf"
 
 
#endif //_INCRON_H_