Subversion Repositories public

Rev

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