Subversion Repositories public

Rev

Rev 79 | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 79 Rev 84
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, 2007 Lukas Jelinek, <lukas@aiken.cz>
8
 * Copyright (C) 2006, 2007 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 <fcntl.h>
19
#include <fcntl.h>
20
#include <pwd.h>
20
#include <pwd.h>
21
#include <dirent.h>
21
#include <dirent.h>
22
#include <syslog.h>
22
#include <syslog.h>
23
#include <errno.h>
23
#include <errno.h>
24
#include <sys/poll.h>
24
#include <sys/poll.h>
25
#include <sys/stat.h>
25
#include <sys/stat.h>
26
26
27
#include "inotify-cxx.h"
27
#include "inotify-cxx.h"
28
#include "appinst.h"
28
#include "appinst.h"
29
#include "appargs.h"
29
#include "appargs.h"
30
30
31
#include "incron.h"
31
#include "incron.h"
32
#include "incrontab.h"
32
#include "incrontab.h"
33
#include "usertable.h"
33
#include "usertable.h"
34
#include "incroncfg.h"
34
#include "incroncfg.h"
35
35
36
36
37
/// Logging options (console as fallback, log PID)
37
/// Logging options (console as fallback, log PID)
38
#define INCRON_LOG_OPTS (LOG_CONS | LOG_PID)
38
#define INCRON_LOG_OPTS (LOG_CONS | LOG_PID)
39
39
40
/// Logging facility (use CRON)
40
/// Logging facility (use CRON)
41
#define INCRON_LOG_FACIL LOG_CRON
41
#define INCRON_LOG_FACIL LOG_CRON
42
42
43
/// incrond version string
43
/// incrond version string
44
#define INCROND_VERSION INCROND_NAME " " INCRON_VERSION
44
#define INCROND_VERSION INCROND_NAME " " INCRON_VERSION
45
45
46
/// incrontab description string
46
/// incrontab description string
47
#define INCROND_DESCRIPTION "incrond - inotify cron daemon\n" \
47
#define INCROND_DESCRIPTION "incrond - inotify cron daemon\n" \
48
                            "(c) Lukas Jelinek, 2006, 2007"
48
                            "(c) Lukas Jelinek, 2006, 2007"
49
49
50
/// incrontab help string
50
/// incrontab help string
51
#define INCROND_HELP INCROND_DESCRIPTION "\n\n" \
51
#define INCROND_HELP INCROND_DESCRIPTION "\n\n" \
52
          "usage: incrond [<options>]\n\n" \
52
          "usage: incrond [<options>]\n\n" \
53
          "<operation> may be one of the following:\n" \
53
          "<operation> may be one of the following:\n" \
54
          "These options may be used:\n" \
54
          "These options may be used:\n" \
55
          "  -?, --about                  gives short information about program\n" \
55
          "  -?, --about                  gives short information about program\n" \
56
          "  -h, --help                   prints this help text\n" \
56
          "  -h, --help                   prints this help text\n" \
57
          "  -n, --foreground             runs on foreground (no daemonizing)\n" \
57
          "  -n, --foreground             runs on foreground (no daemonizing)\n" \
58
          "  -k, --kill                   terminates running instance of incrond\n" \
58
          "  -k, --kill                   terminates running instance of incrond\n" \
59
          "  -f <FILE>, --config=<FILE>   overrides default configuration file  (requires root privileges)\n" \
59
          "  -f <FILE>, --config=<FILE>   overrides default configuration file  (requires root privileges)\n" \
60
          "  -V, --version                prints program version\n\n" \
60
          "  -V, --version                prints program version\n\n" \
61
          "For reporting bugs please use http:://bts.aiken.cz\n"
61
          "For reporting bugs please use http:://bts.aiken.cz\n"
62
62
63
63
64
64
65
/// User name to user table mapping table
65
/// User name to user table mapping table
66
SUT_MAP g_ut;
66
SUT_MAP g_ut;
67
67
68
/// Finish program yes/no
68
/// Finish program yes/no
69
volatile bool g_fFinish = false;
69
volatile bool g_fFinish = false;
70
70
71
/// Pipe for notifying about dead children
71
/// Pipe for notifying about dead children
72
int g_cldPipe[2];
72
int g_cldPipe[2];
73
73
74
// Buffer for emptying child pipe
74
// Buffer for emptying child pipe
75
#define CHILD_PIPE_BUF_LEN 32
75
#define CHILD_PIPE_BUF_LEN 32
76
char g_cldPipeBuf[CHILD_PIPE_BUF_LEN];
76
char g_cldPipeBuf[CHILD_PIPE_BUF_LEN];
77
77
78
/// Daemonize true/false
78
/// Daemonize true/false
79
bool g_daemon = true;
79
bool g_daemon = true;
80
80
81
/// Handles a signal.
81
/// Handles a signal.
82
/**
82
/**
83
 * For SIGTERM and SIGINT it sets the program finish variable.
83
 * For SIGTERM and SIGINT it sets the program finish variable.
84
 * For SIGCHLD it writes a character into the notification pipe
84
 * For SIGCHLD it writes a character into the notification pipe
85
 * (this is a workaround made due to disability to reliably
85
 * (this is a workaround made due to disability to reliably
86
 * wait for dead children).
86
 * wait for dead children).
87
 *
87
 *
88
 * \param[in] signo signal number
88
 * \param[in] signo signal number
89
 */
89
 */
90
void on_signal(int signo)
90
void on_signal(int signo)
91
{
91
{
92
  switch (signo) {
92
  switch (signo) {
93
    case SIGTERM:
93
    case SIGTERM:
94
    case SIGINT:
94
    case SIGINT:
95
      g_fFinish = true;
95
      g_fFinish = true;
96
      break;
96
      break;
97
    case SIGCHLD:
97
    case SIGCHLD:
98
      // first empty pipe (to prevent internal buffer overflow)
98
      // first empty pipe (to prevent internal buffer overflow)
99
      do {} while (read(g_cldPipe[0], g_cldPipeBuf, CHILD_PIPE_BUF_LEN) > 0);
99
      do {} while (read(g_cldPipe[0], g_cldPipeBuf, CHILD_PIPE_BUF_LEN) > 0);
100
     
100
     
101
      // now write one character
101
      // now write one character
102
      write(g_cldPipe[1], "X", 1);
102
      write(g_cldPipe[1], "X", 1);
103
      break;
103
      break;
104
    default:;
104
    default:;
105
  }
105
  }
106
}
106
}
107
107
108
108
109
109
110
110
111
/// Attempts to load all (user and system) incron tables.
111
/// Attempts to load all (user and system) incron tables.
112
/**
112
/**
113
 * Loaded tables are registered for processing events.
113
 * Loaded tables are registered for processing events.
114
 *
114
 *
115
 * \param[in] pEd inotify event dispatcher
115
 * \param[in] pEd inotify event dispatcher
116
 *
116
 *
117
 * \throw InotifyException thrown if base table directory cannot be read
117
 * \throw InotifyException thrown if base table directory cannot be read
118
 */
118
 */
119
void load_tables(EventDispatcher* pEd) throw (InotifyException)
119
void load_tables(EventDispatcher* pEd) throw (InotifyException)
120
{
120
{
121
  // WARNING - this function has not been optimized!!!
121
  // WARNING - this function has not been optimized!!!
122
 
122
 
123
  std::string s;
123
  std::string s;
124
  if (!IncronCfg::GetValue("system_table_dir", s))
124
  if (!IncronCfg::GetValue("system_table_dir", s))
125
    throw InotifyException("configuration system is corrupted", EINVAL);
125
    throw InotifyException("configuration system is corrupted", EINVAL);
126
 
126
 
127
  DIR* d = opendir(s.c_str());
127
  DIR* d = opendir(s.c_str());
128
  if (d != NULL) {
128
  if (d != NULL) {
129
    syslog(LOG_NOTICE, "loading system tables");
129
    syslog(LOG_NOTICE, "loading system tables");
130
     
130
     
131
    struct dirent* pDe = NULL;
131
    struct dirent* pDe = NULL;
132
    while ((pDe = readdir(d)) != NULL) {
132
    while ((pDe = readdir(d)) != NULL) {
133
      std::string un(pDe->d_name);
133
      std::string un(pDe->d_name);
134
      std::string path(IncronCfg::BuildPath(s, pDe->d_name));
134
      std::string path(IncronCfg::BuildPath(s, pDe->d_name));
135
     
135
     
136
      bool ok = pDe->d_type == DT_REG;
136
      bool ok = pDe->d_type == DT_REG;
137
      if (pDe->d_type == DT_UNKNOWN) {
137
      if (pDe->d_type == DT_UNKNOWN) {
138
        struct stat st;
138
        struct stat st;
139
        if (stat(path.c_str(), &st) == 0)
139
        if (stat(path.c_str(), &st) == 0)
140
          ok = S_ISREG(st.st_mode);
140
          ok = S_ISREG(st.st_mode);
141
      }
141
      }
142
     
142
     
143
      if (ok) {
143
      if (ok) {
144
        syslog(LOG_INFO, "loading table %s", pDe->d_name);
144
        syslog(LOG_INFO, "loading table %s", pDe->d_name);
145
        UserTable* pUt = new UserTable(pEd, un, true);
145
        UserTable* pUt = new UserTable(pEd, un, true);
146
        g_ut.insert(SUT_MAP::value_type(path, pUt));
146
        g_ut.insert(SUT_MAP::value_type(path, pUt));
147
        pUt->Load();
147
        pUt->Load();
148
      }
148
      }
149
    }
149
    }
150
   
150
   
151
    closedir(d);
151
    closedir(d);
152
  }
152
  }
153
  else {
153
  else {
154
    syslog(LOG_WARNING, "cannot open system table directory (ignoring)");
154
    syslog(LOG_WARNING, "cannot open system table directory (ignoring)");
155
  }
155
  }
156
 
156
 
157
  if (!IncronCfg::GetValue("user_table_dir", s))
157
  if (!IncronCfg::GetValue("user_table_dir", s))
158
    throw InotifyException("configuration system is corrupted", EINVAL);
158
    throw InotifyException("configuration system is corrupted", EINVAL);
159
   
159
   
160
  d = opendir(s.c_str());
160
  d = opendir(s.c_str());
161
  if (d == NULL)
161
  if (d == NULL)
162
    throw InotifyException("cannot open user table directory", errno);
162
    throw InotifyException("cannot open user table directory", errno);
163
 
163
 
164
  syslog(LOG_NOTICE, "loading user tables");
164
  syslog(LOG_NOTICE, "loading user tables");
165
   
165
   
166
  struct dirent* pDe = NULL;
166
  struct dirent* pDe = NULL;
167
  while ((pDe = readdir(d)) != NULL) {
167
  while ((pDe = readdir(d)) != NULL) {
168
    std::string un(pDe->d_name);
168
    std::string un(pDe->d_name);
169
    std::string path(IncronCfg::BuildPath(s, pDe->d_name));
169
    std::string path(IncronCfg::BuildPath(s, pDe->d_name));
170
   
170
   
171
    bool ok = pDe->d_type == DT_REG;
171
    bool ok = pDe->d_type == DT_REG;
172
    if (pDe->d_type == DT_UNKNOWN) {
172
    if (pDe->d_type == DT_UNKNOWN) {
173
      struct stat st;
173
      struct stat st;
174
      if (stat(path.c_str(), &st) == 0)
174
      if (stat(path.c_str(), &st) == 0)
175
        ok = S_ISREG(st.st_mode);
175
        ok = S_ISREG(st.st_mode);
176
    }
176
    }
177
   
177
   
178
    if (ok) {
178
    if (ok) {
179
      if (UserTable::CheckUser(pDe->d_name)) {
179
      if (UserTable::CheckUser(pDe->d_name)) {
180
        syslog(LOG_INFO, "loading table for user %s", pDe->d_name);
180
        syslog(LOG_INFO, "loading table for user %s", pDe->d_name);
181
        UserTable* pUt = new UserTable(pEd, un, false);
181
        UserTable* pUt = new UserTable(pEd, un, false);
182
        g_ut.insert(SUT_MAP::value_type(path, pUt));
182
        g_ut.insert(SUT_MAP::value_type(path, pUt));
183
        pUt->Load();
183
        pUt->Load();
184
      }
184
      }
185
      else {
185
      else {
186
        syslog(LOG_WARNING, "table for invalid user %s found (ignored)", pDe->d_name);
186
        syslog(LOG_WARNING, "table for invalid user %s found (ignored)", pDe->d_name);
187
      }
187
      }
188
    }
188
    }
189
  }
189
  }
190
 
190
 
191
  closedir(d);
191
  closedir(d);
192
}
192
}
193
193
194
/// Deallocates all memory used by incron tables and unregisters them from the dispatcher.
194
/// Deallocates all memory used by incron tables and unregisters them from the dispatcher.
195
/**
195
/**
196
 * \param[in] pEd event dispatcher
196
 * \param[in] pEd event dispatcher
197
 */
197
 */
198
void free_tables(EventDispatcher* pEd)
198
void free_tables(EventDispatcher* pEd)
199
{
199
{
200
  pEd->Clear();
200
  pEd->Clear();
201
 
201
 
202
  SUT_MAP::iterator it = g_ut.begin();
202
  SUT_MAP::iterator it = g_ut.begin();
203
  while (it != g_ut.end()) {
203
  while (it != g_ut.end()) {
204
    UserTable* pUt = (*it).second;
204
    UserTable* pUt = (*it).second;
205
    delete pUt;
205
    delete pUt;
206
    it++;
206
    it++;
207
  }
207
  }
208
 
208
 
209
  g_ut.clear();
209
  g_ut.clear();
210
}
210
}
211
211
212
/// Prepares a 'dead/done child' notification pipe.
212
/// Prepares a 'dead/done child' notification pipe.
213
/**
213
/**
214
 * This function returns no value at all and on error it
214
 * This function returns no value at all and on error it
215
 * throws an exception.
215
 * throws an exception.
216
 */
216
 */
217
void prepare_pipe()
217
void prepare_pipe()
218
{
218
{
219
  g_cldPipe[0] = -1;
219
  g_cldPipe[0] = -1;
220
  g_cldPipe[1] = -1;
220
  g_cldPipe[1] = -1;
221
 
221
 
222
  if (pipe(g_cldPipe) != 0)
222
  if (pipe(g_cldPipe) != 0)
223
      throw InotifyException("cannot create notification pipe", errno, NULL);
223
      throw InotifyException("cannot create notification pipe", errno, NULL);
224
 
224
 
225
  for (int i=0; i<2; i++) {
225
  for (int i=0; i<2; i++) {
226
    int res = fcntl(g_cldPipe[i], F_GETFL);
226
    int res = fcntl(g_cldPipe[i], F_GETFL);
227
    if (res == -1)
227
    if (res == -1)
228
      throw InotifyException("cannot get pipe flags", errno, NULL);
228
      throw InotifyException("cannot get pipe flags", errno, NULL);
229
   
229
   
230
    res |= O_NONBLOCK;
230
    res |= O_NONBLOCK;
231
       
231
       
232
    if (fcntl(g_cldPipe[i], F_SETFL, res) == -1)
232
    if (fcntl(g_cldPipe[i], F_SETFL, res) == -1)
233
      throw InotifyException("cannot set pipe flags", errno, NULL);
233
      throw InotifyException("cannot set pipe flags", errno, NULL);
234
     
234
     
235
    res = fcntl(g_cldPipe[i], F_GETFD);
235
    res = fcntl(g_cldPipe[i], F_GETFD);
236
    if (res == -1)
236
    if (res == -1)
237
      throw InotifyException("cannot get pipe flags", errno, NULL);
237
      throw InotifyException("cannot get pipe flags", errno, NULL);
238
   
238
   
239
    res |= FD_CLOEXEC;
239
    res |= FD_CLOEXEC;
240
       
240
       
241
    if (fcntl(g_cldPipe[i], F_SETFD, res) == -1)
241
    if (fcntl(g_cldPipe[i], F_SETFD, res) == -1)
242
      throw InotifyException("cannot set pipe flags", errno, NULL);
242
      throw InotifyException("cannot set pipe flags", errno, NULL);
243
  }
243
  }
244
}
244
}
245
245
246
/// Checks whether a parameter string is a specific command.
246
/// Checks whether a parameter string is a specific command.
247
/**
247
/**
248
 * The string is accepted if it equals either the short or long
248
 * The string is accepted if it equals either the short or long
249
 * form of the command.
249
 * form of the command.
250
 *
250
 *
251
 * \param[in] s checked string
251
 * \param[in] s checked string
252
 * \param[in] shortCmd short form of command
252
 * \param[in] shortCmd short form of command
253
 * \param[in] longCmd long form of command
253
 * \param[in] longCmd long form of command
254
 * \return true = string accepted, false = otherwise
254
 * \return true = string accepted, false = otherwise
255
 */  
255
 */  
256
 /*
256
 /*
257
bool check_parameter(const char* s, const char* shortCmd, const char* longCmd)
257
bool check_parameter(const char* s, const char* shortCmd, const char* longCmd)
258
{
258
{
259
  return strcmp(s, shortCmd)  == 0
259
  return strcmp(s, shortCmd)  == 0
260
      || strcmp(s, longCmd)   == 0;
260
      || strcmp(s, longCmd)   == 0;
261
}
261
}
262
*/
262
*/
263
263
264
/// Initializes a poll array.
264
/// Initializes a poll array.
265
/**
265
/**
266
 * \param[out] pfd poll structure array
266
 * \param[out] pfd poll structure array
267
 * \param[in] pipefd pipe file descriptor
267
 * \param[in] pipefd pipe file descriptor
268
 * \param[in] infd inotify infrastructure file descriptor
268
 * \param[in] infd inotify infrastructure file descriptor
269
 */
269
 */
270
void init_poll_array(struct pollfd pfd[], int pipefd, int infd)
270
void init_poll_array(struct pollfd pfd[], int pipefd, int infd)
271
{
271
{
272
  pfd[0].fd = pipefd;
272
  pfd[0].fd = pipefd;
273
  pfd[0].events = (short) POLLIN;
273
  pfd[0].events = (short) POLLIN;
274
  pfd[0].revents = (short) 0;
274
  pfd[0].revents = (short) 0;
275
  pfd[1].fd = infd;
275
  pfd[1].fd = infd;
276
  pfd[1].events = (short) POLLIN;
276
  pfd[1].events = (short) POLLIN;
277
  pfd[1].revents = (short) 0;
277
  pfd[1].revents = (short) 0;
278
}
278
}
279
279
280
280
281
/// Main application function.
281
/// Main application function.
282
/**
282
/**
283
 * \param[in] argc argument count
283
 * \param[in] argc argument count
284
 * \param[in] argv argument array
284
 * \param[in] argv argument array
285
 * \return 0 on success, 1 on error
285
 * \return 0 on success, 1 on error
286
 *
286
 *
287
 * \attention In daemon mode, it finishes immediately.
287
 * \attention In daemon mode, it finishes immediately.
288
 */
288
 */
289
int main(int argc, char** argv)
289
int main(int argc, char** argv)
290
{
290
{
291
  AppArgs::Init();
291
  AppArgs::Init();
292
292
293
  if (!(  AppArgs::AddOption("about",       '?', AAT_NO_VALUE, false)
293
  if (!(  AppArgs::AddOption("about",       '?', AAT_NO_VALUE, false)
294
      &&  AppArgs::AddOption("help",        'h', AAT_NO_VALUE, false)
294
      &&  AppArgs::AddOption("help",        'h', AAT_NO_VALUE, false)
295
      &&  AppArgs::AddOption("foreground",  'n', AAT_NO_VALUE, false)
295
      &&  AppArgs::AddOption("foreground",  'n', AAT_NO_VALUE, false)
296
      &&  AppArgs::AddOption("kill",        'k', AAT_NO_VALUE, false)
296
      &&  AppArgs::AddOption("kill",        'k', AAT_NO_VALUE, false)
297
      &&  AppArgs::AddOption("config",      'f', AAT_MANDATORY_VALUE, false)
297
      &&  AppArgs::AddOption("config",      'f', AAT_MANDATORY_VALUE, false)
298
      &&  AppArgs::AddOption("version",     'V', AAT_NO_VALUE, false)))
298
      &&  AppArgs::AddOption("version",     'V', AAT_NO_VALUE, false)))
299
  {
299
  {
300
    fprintf(stderr, "error while initializing application");
300
    fprintf(stderr, "error while initializing application");
301
    return 1;
301
    return 1;
302
  }
302
  }
303
 
303
 
304
  AppArgs::Parse(argc, argv);
304
  AppArgs::Parse(argc, argv);
305
 
305
 
306
  if (AppArgs::ExistsOption("help")) {
306
  if (AppArgs::ExistsOption("help")) {
307
    fprintf(stderr, "%s\n", INCROND_HELP);
307
    fprintf(stderr, "%s\n", INCROND_HELP);
308
    return 0;
308
    return 0;
309
  }
309
  }
310
 
310
 
311
  if (AppArgs::ExistsOption("about")) {
311
  if (AppArgs::ExistsOption("about")) {
312
    fprintf(stderr, "%s\n", INCROND_DESCRIPTION);
312
    fprintf(stderr, "%s\n", INCROND_DESCRIPTION);
313
    return 0;
313
    return 0;
314
  }
314
  }
315
 
315
 
316
  if (AppArgs::ExistsOption("version")) {
316
  if (AppArgs::ExistsOption("version")) {
317
    fprintf(stderr, "%s\n", INCROND_VERSION);
317
    fprintf(stderr, "%s\n", INCROND_VERSION);
318
    return 0;
318
    return 0;
319
  }
319
  }
320
 
320
 
321
  IncronCfg::Init();
321
  IncronCfg::Init();
322
 
322
 
323
  std::string cfg;
323
  std::string cfg;
324
  if (!AppArgs::GetOption("config", cfg))
324
  if (!AppArgs::GetOption("config", cfg))
325
    cfg = INCRON_CONFIG;
325
    cfg = INCRON_CONFIG;
326
  IncronCfg::Load(cfg);
326
  IncronCfg::Load(cfg);
327
 
327
 
328
  std::string lckdir;
328
  std::string lckdir;
329
  IncronCfg::GetValue("lockfile_dir", lckdir);
329
  IncronCfg::GetValue("lockfile_dir", lckdir);
330
  std::string lckfile;
330
  std::string lckfile;
331
  IncronCfg::GetValue("lockfile_name", lckfile);
331
  IncronCfg::GetValue("lockfile_name", lckfile);
332
  AppInstance app(lckfile, lckdir);
332
  AppInstance app(lckfile, lckdir);
333
 
333
 
334
  if (AppArgs::ExistsOption("kill")) {
334
  if (AppArgs::ExistsOption("kill")) {
335
    fprintf(stderr, "attempting to terminate a running instance of incrond...\n");
335
    fprintf(stderr, "attempting to terminate a running instance of incrond...\n");
336
    if (app.Terminate()) {
336
    if (app.Terminate()) {
337
      fprintf(stderr, "the instance notified, going down\n");
337
      fprintf(stderr, "the instance notified, going down\n");
338
      return 0;
338
      return 0;
339
    }
339
    }
340
    else {
340
    else {
341
      fprintf(stderr, "error - incrond probably not running\n");
341
      fprintf(stderr, "error - incrond probably not running\n");
342
      return 1;
342
      return 1;
343
    }
343
    }
344
  }
344
  }
345
 
345
 
346
  if (AppArgs::ExistsOption("foreground"))
346
  if (AppArgs::ExistsOption("foreground"))
347
    g_daemon = false;
347
    g_daemon = false;
348
 
348
 
349
 
349
 
350
  openlog(INCROND_NAME, INCRON_LOG_OPTS, INCRON_LOG_FACIL);
350
  openlog(INCROND_NAME, INCRON_LOG_OPTS, INCRON_LOG_FACIL);
351
 
351
 
352
  syslog(LOG_NOTICE, "starting service (version %s, built on %s %s)", INCRON_VERSION, __DATE__, __TIME__);
352
  syslog(LOG_NOTICE, "starting service (version %s, built on %s %s)", INCRON_VERSION, __DATE__, __TIME__);
353
 
353
 
354
  AppArgs::Destroy();
354
  AppArgs::Destroy();
355
 
355
 
356
  int ret = 0;
356
  int ret = 0;
357
 
357
 
358
  std::string sysBase;
358
  std::string sysBase;
359
  std::string userBase;
359
  std::string userBase;
360
 
360
 
361
  if (!IncronCfg::GetValue("system_table_dir", sysBase))
361
  if (!IncronCfg::GetValue("system_table_dir", sysBase))
362
    throw InotifyException("configuration is corrupted", EINVAL);
362
    throw InotifyException("configuration is corrupted", EINVAL);
363
 
363
 
364
  if (access(sysBase.c_str(), R_OK) != 0) {
364
  if (access(sysBase.c_str(), R_OK) != 0) {
365
    syslog(LOG_CRIT, "cannot read directory for system tables (%s): (%i) %s", sysBase.c_str(), errno, strerror(errno));
365
    syslog(LOG_CRIT, "cannot read directory for system tables (%s): (%i) %s", sysBase.c_str(), errno, strerror(errno));
366
    if (!g_daemon)
366
    if (!g_daemon)
367
        fprintf(stderr, "cannot read directory for system tables (%s): (%i) %s", sysBase.c_str(), errno, strerror(errno));
367
        fprintf(stderr, "cannot read directory for system tables (%s): (%i) %s", sysBase.c_str(), errno, strerror(errno));
368
    ret = 1;
368
    ret = 1;
369
    goto error;
369
    goto error;
370
  }
370
  }
371
 
371
 
372
  if (!IncronCfg::GetValue("user_table_dir", userBase))
372
  if (!IncronCfg::GetValue("user_table_dir", userBase))
373
    throw InotifyException("configuration is corrupted", EINVAL);
373
    throw InotifyException("configuration is corrupted", EINVAL);
374
 
374
 
375
  if (access(userBase.c_str(), R_OK) != 0) {
375
  if (access(userBase.c_str(), R_OK) != 0) {
376
    syslog(LOG_CRIT, "cannot read directory for user tables (%s): (%i) %s", userBase.c_str(), errno, strerror(errno));
376
    syslog(LOG_CRIT, "cannot read directory for user tables (%s): (%i) %s", userBase.c_str(), errno, strerror(errno));
377
    if (!g_daemon)
377
    if (!g_daemon)
378
        fprintf(stderr, "cannot read directory for user tables (%s): (%i) %s", userBase.c_str(), errno, strerror(errno));
378
        fprintf(stderr, "cannot read directory for user tables (%s): (%i) %s", userBase.c_str(), errno, strerror(errno));
379
    ret = 1;
379
    ret = 1;
380
    goto error;
380
    goto error;
381
  }
381
  }
382
 
382
 
383
  try {
383
  try {
384
    if (g_daemon)
384
    if (g_daemon)
385
      daemon(0, 0);
385
      daemon(0, 0);
386
 
386
 
387
    try {
387
    try {
388
    if (!app.Lock()) {
388
    if (!app.Lock()) {
389
      syslog(LOG_CRIT, "another instance of incrond already running");
389
      syslog(LOG_CRIT, "another instance of incrond already running");
390
      if (!g_daemon)
390
      if (!g_daemon)
391
        fprintf(stderr, "another instance of incrond already running\n");
391
        fprintf(stderr, "another instance of incrond already running\n");
392
      ret = 1;
392
      ret = 1;
393
      goto error;
393
      goto error;
394
      }
394
      }
395
    } catch (AppInstException e) {
395
    } catch (AppInstException e) {
396
      syslog(LOG_CRIT, "instance lookup failed: (%i) %s", e.GetErrorNumber(), strerror(e.GetErrorNumber()));
396
      syslog(LOG_CRIT, "instance lookup failed: (%i) %s", e.GetErrorNumber(), strerror(e.GetErrorNumber()));
397
      if (!g_daemon)
397
      if (!g_daemon)
398
        fprintf(stderr, "instance lookup failed: (%i) %s\n", e.GetErrorNumber(), strerror(e.GetErrorNumber()));
398
        fprintf(stderr, "instance lookup failed: (%i) %s\n", e.GetErrorNumber(), strerror(e.GetErrorNumber()));
399
      ret = 1;
399
      ret = 1;
400
      goto error;
400
      goto error;
401
    }
401
    }
402
   
402
   
403
    prepare_pipe();
403
    prepare_pipe();
404
   
404
   
405
    Inotify in;
405
    Inotify in;
406
    in.SetNonBlock(true);
406
    in.SetNonBlock(true);
407
    in.SetCloseOnExec(true);
407
    in.SetCloseOnExec(true);
408
   
408
   
409
    uint32_t wm = IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_MOVE | IN_DELETE_SELF | IN_UNMOUNT;
409
    uint32_t wm = IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_MOVE | IN_DELETE_SELF | IN_UNMOUNT;
410
    InotifyWatch stw(sysBase, wm);
410
    InotifyWatch stw(sysBase, wm);
411
    in.Add(stw);
411
    in.Add(stw);
412
    InotifyWatch utw(userBase, wm);
412
    InotifyWatch utw(userBase, wm);
413
    in.Add(utw);
413
    in.Add(utw);
414
   
414
   
415
    EventDispatcher ed(g_cldPipe[0], &in, &stw, &utw);
415
    EventDispatcher ed(g_cldPipe[0], &in, &stw, &utw);
416
   
416
   
417
    try {
417
    try {
418
      load_tables(&ed);
418
      load_tables(&ed);
419
    } catch (InotifyException e) {
419
    } catch (InotifyException e) {
420
      int err = e.GetErrorNumber();
420
      int err = e.GetErrorNumber();
421
      syslog(LOG_CRIT, "%s: (%i) %s", e.GetMessage().c_str(), err, strerror(err));
421
      syslog(LOG_CRIT, "%s: (%i) %s", e.GetMessage().c_str(), err, strerror(err));
422
      ret = 1;
422
      ret = 1;
423
      goto error;
423
      goto error;
424
    }
424
    }
425
   
425
   
426
    ed.Rebuild(); // not too efficient, but simple 
426
    ed.Rebuild(); // not too efficient, but simple 
427
   
427
   
428
    signal(SIGTERM, on_signal);
428
    signal(SIGTERM, on_signal);
429
    signal(SIGINT, on_signal);
429
    signal(SIGINT, on_signal);
430
    signal(SIGCHLD, on_signal);
430
    signal(SIGCHLD, on_signal);
431
   
431
   
432
    syslog(LOG_NOTICE, "ready to process filesystem events");
432
    syslog(LOG_NOTICE, "ready to process filesystem events");
433
   
433
   
434
    while (!g_fFinish) {
434
    while (!g_fFinish) {
435
     
435
     
436
      int res = poll(ed.GetPollData(), ed.GetSize(), -1);
436
      int res = poll(ed.GetPollData(), ed.GetSize(), -1);
437
     
437
     
438
      if (res > 0) {
438
      if (res > 0) {
439
        if (ed.ProcessEvents())
439
        if (ed.ProcessEvents())
440
          UserTable::FinishDone();
440
          UserTable::FinishDone();
441
      }
441
      }
442
      else if (res < 0) {
442
      else if (res < 0) {
443
        if (errno != EINTR)
443
        if (errno != EINTR)
444
          throw InotifyException("polling failed", errno, NULL);
444
          throw InotifyException("polling failed", errno, NULL);
445
      }
445
      }
446
     
446
     
447
    }
447
    }
448
   
448
   
449
    free_tables(&ed);
449
    free_tables(&ed);
450
   
450
   
451
    if (g_cldPipe[0] != -1)
451
    if (g_cldPipe[0] != -1)
452
      close(g_cldPipe[0]);
452
      close(g_cldPipe[0]);
453
    if (g_cldPipe[1] != -1)
453
    if (g_cldPipe[1] != -1)
454
      close(g_cldPipe[1]);
454
      close(g_cldPipe[1]);
455
  } catch (InotifyException e) {
455
  } catch (InotifyException e) {
456
    int err = e.GetErrorNumber();
456
    int err = e.GetErrorNumber();
457
    syslog(LOG_CRIT, "*** unhandled exception occurred ***");
457
    syslog(LOG_CRIT, "*** unhandled exception occurred ***");
458
    syslog(LOG_CRIT, "  %s", e.GetMessage().c_str());
458
    syslog(LOG_CRIT, "  %s", e.GetMessage().c_str());
459
    syslog(LOG_CRIT, "  error: (%i) %s", err, strerror(err));
459
    syslog(LOG_CRIT, "  error: (%i) %s", err, strerror(err));
460
    ret = 1;
460
    ret = 1;
461
  }
461
  }
462
462
463
error:
463
error:
464
464
465
  syslog(LOG_NOTICE, "stopping service");
465
  syslog(LOG_NOTICE, "stopping service");
466
 
466
 
467
  closelog();
467
  closelog();
468
 
468
 
469
  return ret;
469
  return ret;
470
}
470
}
471
 
471