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 |