Subversion Repositories public

Rev

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

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