Subversion Repositories public

Rev

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