Subversion Repositories public

Rev

Rev 45 | Rev 49 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

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