Subversion Repositories public

Rev

Rev 45 | Rev 49 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
45 luk 1
 
2
/// inotify cron daemon main file
3
/**
4
 * \file icd-main.cpp
5
 *
6
 * inotify cron system
7
 *
8
 * Copyright (C) 2006 Lukas Jelinek, <lukas@aiken.cz>
9
 *
10
 * This program is free software; you can use it, redistribute
11
 * it and/or modify it under the terms of the GNU General Public
12
 * License, version 2 (see LICENSE-GPL).
13
 *  
14
 */
15
 
16
#include <map>
17
#include <signal.h>
18
#include <wait.h>
19
#include <pwd.h>
20
#include <dirent.h>
21
#include <syslog.h>
22
#include <errno.h>
47 luk 23
#include <sys/poll.h>
45 luk 24
 
25
#include "inotify-cxx.h"
26
#include "incrontab.h"
27
 
28
#include "usertable.h"
29
 
47 luk 30
#define DAEMON true
45 luk 31
 
32
#define INCRON_APP_NAME "incrond"
33
#define INCRON_LOG_OPTS (LOG_CONS | LOG_PID)
34
#define INCRON_LOG_FACIL LOG_CRON
35
 
36
 
37
typedef std::map<std::string, UserTable*> SUT_MAP;
38
 
39
 
40
SUT_MAP g_ut;
41
 
47 luk 42
volatile bool g_fFinish = false;
45 luk 43
 
47 luk 44
/// Handles a signal.
45
/**
46
 * For SIGTERM and SIGINT it sets the program finish variable.
47
 *
48
 * \param[in] signo signal number
49
 */
45 luk 50
void on_signal(int signo)
51
{
47 luk 52
  if (signo == SIGTERM || signo == SIGINT)
53
    g_fFinish = true;
45 luk 54
}
55
 
47 luk 56
/// Checks whether an user exists and has permission to use incron.
57
/**
58
 * It searches for the given user name in the user database.
59
 * If it failes it returns 'false'. Otherwise it checks
60
 * permission files for this user (see InCronTab::CheckUser()).
61
 *
62
 * \param[in] user user name
63
 * \return true = user has permission to use incron, false = otherwise
64
 *
65
 * \sa InCronTab::CheckUser()
66
 */
45 luk 67
bool check_user(const char* user)
68
{
69
  struct passwd* pw = getpwnam(user);
70
  if (pw == NULL)
71
    return false;
72
 
73
  return InCronTab::CheckUser(user);
74
}
75
 
47 luk 76
/// Attempts to load all user incron tables.
77
/**
78
 * Loaded tables are registered for processing events.
79
 *
80
 * \param[in] pIn inotify object
81
 * \param[in] pEd inotify event dispatcher
82
 *
83
 * \throw InotifyException thrown if base table directory cannot be read
84
 */
85
void load_tables(Inotify* pIn, EventDispatcher* pEd) throw (InotifyException)
45 luk 86
{
87
  DIR* d = opendir(INCRON_TABLE_BASE);
47 luk 88
  if (d == NULL)
89
    throw InotifyException("cannot open table directory", errno);
45 luk 90
 
91
  syslog(LOG_NOTICE, "loading user tables");
92
 
93
  struct dirent* pDe = NULL;
94
  while ((pDe = readdir(d)) != NULL) {
95
    std::string un(pDe->d_name);
96
    if (pDe->d_type == DT_REG && un != "." && un != "..") {
97
      if (check_user(pDe->d_name)) {
98
        syslog(LOG_INFO, "loading table for user %s", pDe->d_name);
99
        UserTable* pUt = new UserTable(pIn, pEd, un);
100
        g_ut.insert(SUT_MAP::value_type(un, pUt));
101
        pUt->Load();
102
      }
103
      else {
104
        syslog(LOG_WARNING, "table for invalid user %s found (ignored)", pDe->d_name);
105
      }
106
    }
107
  }
108
 
109
  closedir(d);
110
}
111
 
47 luk 112
/// Main application function.
113
/**
114
 * \param[in] argc argument count
115
 * \param[in] argv argument array
116
 * \return 0 on success, 1 on error
117
 *
118
 * \attention In daemon mode, it finishes immediately.
119
 */
45 luk 120
int main(int argc, char** argv)
121
{
122
  openlog(INCRON_APP_NAME, INCRON_LOG_OPTS, INCRON_LOG_FACIL);
123
 
124
  syslog(LOG_NOTICE, "starting service");
125
 
47 luk 126
  try {
127
    Inotify in;
128
    in.SetNonBlock(true);
129
 
130
    EventDispatcher ed(&in);
131
 
132
    try {
133
      load_tables(&in, &ed);
134
    } catch (InotifyException e) {
135
      int err = e.GetErrorNumber();
136
      syslog(LOG_CRIT, "%s: (%i) %s", e.GetMessage().c_str(), err, strerror(err));
137
      syslog(LOG_NOTICE, "stopping service");
138
      closelog();
139
      return 1;
140
    }
141
 
142
    signal(SIGTERM, on_signal);
143
    signal(SIGINT, on_signal);
144
    signal(SIGCHLD, on_signal);
145
 
146
    if (DAEMON)
147
      daemon(0, 0);
148
 
149
    uint32_t wm = IN_CLOSE_WRITE | IN_DELETE | IN_MOVE | IN_DELETE_SELF | IN_UNMOUNT;
150
    InotifyWatch watch(INCRON_TABLE_BASE, wm);
151
    in.Add(watch);
152
 
153
    syslog(LOG_NOTICE, "ready to process filesystem events");
154
 
155
    InotifyEvent e;
156
 
157
    struct pollfd pfd;
158
    pfd.fd = in.GetDescriptor();
159
    pfd.events = POLLIN;
160
    pfd.revents = (short) 0;
161
 
162
    while (!g_fFinish) {
163
 
164
      int res = poll(&pfd, 1, -1);
165
      if (res > 0) {
166
        in.WaitForEvents(true);
167
        UserTable::FinishDone();
168
      }
169
      else if (res < 0) {
170
        if (errno != EINTR)
171
          throw InotifyException("polling failed", errno, NULL);
172
      }
173
 
45 luk 174
      while (in.GetEvent(e)) {
175
 
176
        if (e.GetWatch() == &watch) {
177
          if (e.IsType(IN_DELETE_SELF) || e.IsType(IN_UNMOUNT)) {
178
            syslog(LOG_CRIT, "base directory destroyed, exitting");
179
            g_fFinish = true;
180
          }
181
          else if (!e.GetName().empty()) {
182
            SUT_MAP::iterator it = g_ut.find(e.GetName());
183
            if (it != g_ut.end()) {
184
              UserTable* pUt = (*it).second;
185
              if (e.IsType(IN_CLOSE_WRITE) || e.IsType(IN_MOVED_TO)) {
186
                syslog(LOG_INFO, "table for user %s changed, reloading", e.GetName().c_str());
187
                pUt->Dispose();
188
                pUt->Load();
189
              }
190
              else if (e.IsType(IN_MOVED_FROM) || e.IsType(IN_DELETE)) {
191
                syslog(LOG_INFO, "table for user %s destroyed, removing", e.GetName().c_str());
192
                delete pUt;
193
                g_ut.erase(it);
194
              }
195
            }
196
            else if (e.IsType(IN_CLOSE_WRITE) || e.IsType(IN_MOVED_TO)) {
197
              if (check_user(e.GetName().c_str())) {
198
                syslog(LOG_INFO, "table for user %s created, loading", e.GetName().c_str());
199
                UserTable* pUt = new UserTable(&in, &ed, e.GetName());
200
                g_ut.insert(SUT_MAP::value_type(e.GetName(), pUt));
201
                pUt->Load();
202
              }
203
            }
204
          }
205
        }
206
        else {
207
          ed.DispatchEvent(e);
208
        }
209
      }
210
    }
47 luk 211
  } catch (InotifyException e) {
212
    int err = e.GetErrorNumber();
213
    syslog(LOG_CRIT, "*** unhandled exception occurred ***");
214
    syslog(LOG_CRIT, "  %s", e.GetMessage().c_str());
215
    syslog(LOG_CRIT, "  error: (%i) %s", err, strerror(err));
45 luk 216
  }
217
 
218
  syslog(LOG_NOTICE, "stopping service");
219
 
220
  closelog();
221
 
222
  return 0;
223
}