Subversion Repositories public

Compare Revisions

Ignore whitespace Rev 66 → Rev 67

/incron/trunk/icd-main.cpp
30,8 → 30,6
#include "incrontab.h"
#include "usertable.h"
 
/// Daemon yes/no
#define DAEMON true
 
/// Logging options (console as fallback, log PID)
#define INCRON_LOG_OPTS (LOG_CONS | LOG_PID)
39,10 → 37,19
/// Logging facility (use CRON)
#define INCRON_LOG_FACIL LOG_CRON
 
/// 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>"
 
/// User name to user table mapping definition
typedef std::map<std::string, UserTable*> SUT_MAP;
#define INCROND_VERSION_TEXT INCRON_DAEMON_NAME " " INCRON_VERSION
 
 
/// User name to user table mapping table
SUT_MAP g_ut;
 
56,6 → 63,8
#define CHILD_PIPE_BUF_LEN 32
char g_cldPipeBuf[CHILD_PIPE_BUF_LEN];
 
/// Daemonize true/false
bool g_daemon = true;
 
/// Handles a signal.
/**
84,40 → 93,52
}
}
 
/// 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);
if (pw == NULL)
return false;
return InCronTab::CheckUser(user);
}
 
/// 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)
void load_tables(EventDispatcher* pEd) throw (InotifyException)
{
DIR* d = opendir(INCRON_TABLE_BASE);
// WARNING - this function has not been optimized!!!
DIR* d = opendir(INCRON_SYS_TABLE_BASE);
if (d != NULL) {
syslog(LOG_NOTICE, "loading system tables");
struct dirent* pDe = NULL;
while ((pDe = readdir(d)) != NULL) {
std::string un(pDe->d_name);
bool ok = pDe->d_type == DT_REG;
if (pDe->d_type == DT_UNKNOWN) {
struct stat st;
if (stat(pDe->d_name, &st) == 0)
ok = S_ISREG(st.st_mode);
}
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(std::string(INCRON_SYS_TABLE_BASE) + un, pUt));
pUt->Load();
}
}
closedir(d);
}
else {
syslog(LOG_WARNING, "cannot open system table directory (ignoring)");
}
d = opendir(INCRON_USER_TABLE_BASE);
if (d == NULL)
throw InotifyException("cannot open table directory", errno);
throw InotifyException("cannot open user table directory", errno);
syslog(LOG_NOTICE, "loading user tables");
133,10 → 154,10
}
if (ok) {
if (check_user(pDe->d_name)) {
if (UserTable::CheckUser(pDe->d_name)) {
syslog(LOG_INFO, "loading table for user %s", pDe->d_name);
UserTable* pUt = new UserTable(pIn, pEd, un);
g_ut.insert(SUT_MAP::value_type(un, pUt));
UserTable* pUt = new UserTable(pEd, un, false);
g_ut.insert(SUT_MAP::value_type(std::string(INCRON_USER_TABLE_BASE) + un, pUt));
pUt->Load();
}
else {
170,9 → 191,113
if (fcntl(g_cldPipe[i], F_SETFL, res) == -1)
throw InotifyException("cannot set pipe flags", errno, NULL);
res = fcntl(g_cldPipe[i], F_GETFD);
if (res == -1)
throw InotifyException("cannot get pipe flags", errno, NULL);
res |= FD_CLOEXEC;
if (fcntl(g_cldPipe[i], F_SETFD, res) == -1)
throw InotifyException("cannot set pipe flags", errno, NULL);
}
}
 
/// Checks whether a parameter string is a specific command.
/**
* The string is accepted if it equals either the short or long
* form of the command.
*
* \param[in] s checked string
* \param[in] shortCmd short form of command
* \param[in] longCmd long form of command
* \return true = string accepted, false = otherwise
*/
bool check_parameter(const char* s, const char* shortCmd, const char* longCmd)
{
return strcmp(s, shortCmd) == 0
|| 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[in] pipefd pipe file descriptor
* \param[in] infd inotify infrastructure file descriptor
*/
void init_poll_array(struct pollfd pfd[], int pipefd, int infd)
{
pfd[0].fd = pipefd;
pfd[0].events = (short) POLLIN;
pfd[0].revents = (short) 0;
pfd[1].fd = infd;
pfd[1].events = (short) POLLIN;
pfd[1].revents = (short) 0;
}
 
 
/// Main application function.
/**
* \param[in] argc argument count
183,18 → 308,65
*/
int main(int argc, char** argv)
{
if (argc > 2) {
fprintf(stderr, "error: too many parameters\n");
fprintf(stderr, "give --help or -h for more information\n");
return 1;
}
if (argc == 2) {
if (check_parameter(argv[1], "-h", "--help")) {
printf("%s\n", INCROND_HELP_TEXT);
return 0;
}
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;
}
}
openlog(INCRON_DAEMON_NAME, INCRON_LOG_OPTS, INCRON_LOG_FACIL);
syslog(LOG_NOTICE, "starting service (version %s, built on %s %s)", INCRON_VERSION, __DATE__, __TIME__);
try {
if (g_daemon)
daemon(0, 0);
prepare_pipe();
Inotify in;
in.SetNonBlock(true);
in.SetCloseOnExec(true);
EventDispatcher ed(&in);
uint32_t wm = IN_CLOSE_WRITE | IN_DELETE | IN_MOVE | IN_DELETE_SELF | IN_UNMOUNT;
InotifyWatch stw(INCRON_SYS_TABLE_BASE, wm);
in.Add(stw);
InotifyWatch utw(INCRON_USER_TABLE_BASE, wm);
in.Add(utw);
EventDispatcher ed(g_cldPipe[0], &in, &stw, &utw);
try {
load_tables(&in, &ed);
load_tables(&ed);
} catch (InotifyException e) {
int err = e.GetErrorNumber();
syslog(LOG_CRIT, "%s: (%i) %s", e.GetMessage().c_str(), err, strerror(err));
203,68 → 375,72
return 1;
}
if (DAEMON)
daemon(0, 0);
prepare_pipe();
signal(SIGTERM, on_signal);
signal(SIGINT, on_signal);
signal(SIGCHLD, on_signal);
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[2];
pfd[0].fd = in.GetDescriptor();
pfd[0].events = (short) POLLIN;
pfd[0].revents = (short) 0;
pfd[1].fd = g_cldPipe[0];
pfd[1].events = (short) POLLIN;
pfd[1].revents = (short) 0;
while (!g_fFinish) {
int res = poll(pfd, 2, -1);
int res = poll(ed.GetPollData(), ed.GetSize(), -1);
if (res > 0) {
if (pfd[1].revents & ((short) POLLIN)) {
char c;
while (read(pfd[1].fd, &c, 1) > 0) {}
if (ed.ProcessEvents())
UserTable::FinishDone();
}
else {
in.WaitForEvents(true);
}
}
else if (res < 0) {
if (errno != EINTR)
throw InotifyException("polling failed", errno, NULL);
}
/*
while (in.GetEvent(e)) {
if (e.GetWatch() == &watch) {
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(e.GetName());
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());
syslog(LOG_INFO, "table for user %s destroyed, removing", e.GetName().c_str());
delete pUt;
g_ut.erase(it);
}
272,8 → 448,8
else if (e.IsType(IN_CLOSE_WRITE) || e.IsType(IN_MOVED_TO)) {
if (check_user(e.GetName().c_str())) {
syslog(LOG_INFO, "table for user %s created, loading", e.GetName().c_str());
UserTable* pUt = new UserTable(&in, &ed, e.GetName());
g_ut.insert(SUT_MAP::value_type(e.GetName(), pUt));
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();
}
}
284,6 → 460,8
}
}
*/
}
if (g_cldPipe[0] != -1)