ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/vendor/ircservices-5.1.24/main.c
Revision: 3389
Committed: Fri Apr 25 14:12:15 2014 UTC (9 years, 10 months ago) by michael
Content type: text/x-csrc
File size: 11634 byte(s)
Log Message:
- Imported ircservices-5.1.24

File Contents

# Content
1 /* IRC Services -- main source file.
2 * Copyright (c) 1996-2009 Andrew Church <achurch@achurch.org>
3 * Parts written by Andrew Kempe and others.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /*************************************************************************/
20
21 #include "services.h"
22 #include "databases.h"
23 #include "modules.h"
24 #include "timeout.h"
25 #include <fcntl.h>
26 #include <setjmp.h>
27
28
29 /* Hack for sigsetjmp(); since (at least with glibc, and it shouldn't hurt
30 * anywhere else) sigsetjmp() only works if you don't leave the stack frame
31 * it was called from, we have to call it before calling the signals.c
32 * wrapper. */
33
34 #define DO_SIGSETJMP() do { \
35 static sigjmp_buf buf; \
36 if (!sigsetjmp(buf, 1)) \
37 do_sigsetjmp(&buf); \
38 } while (0)
39
40
41 /******** Global variables! ********/
42
43 /* Command-line options: (note that configuration variables are in init.c) */
44 const char *services_dir = SERVICES_DIR;/* -dir=dirname */
45 int debug = 0; /* -debug */
46 int readonly = 0; /* -readonly */
47 int nofork = 0; /* -nofork */
48 int noexpire = 0; /* -noexpire */
49 int noakill = 0; /* -noakill */
50 int forceload = 0; /* -forceload */
51 int encrypt_all = 0; /* -encrypt-all */
52
53
54 /* Set to 1 while we are linked to the network */
55 int linked = 0;
56
57 /* Set to 1 if we are to quit */
58 int quitting = 0;
59
60 /* Set to 1 if we are to quit after saving databases */
61 int delayed_quit = 0;
62
63 /* Set to 1 if we are to restart */
64 int restart = 0;
65
66 /* Contains a message as to why services is terminating */
67 char quitmsg[BUFSIZE] = "";
68
69 /* Input buffer - global, so we can dump it if something goes wrong */
70 char inbuf[BUFSIZE];
71
72 /* Socket for talking to server */
73 Socket *servsock = NULL;
74
75 /* Should we update the databases now? */
76 int save_data = 0;
77
78 /* At what time were we started? */
79 time_t start_time;
80
81 /* Were we unable to open the log? (and the error that occurred) */
82 int openlog_failed, openlog_errno;
83
84 /* Module callbacks (global so init.c can set them): */
85 int cb_connect = -1;
86 int cb_save_complete = -1;
87
88
89 /*************************************************************************/
90 /*************************************************************************/
91
92 /* Callbacks for uplink IRC server socket. */
93
94 /*************************************************************************/
95
96 /* Actions to perform when connection to server completes. */
97
98 void connect_callback(Socket *s, void *param_unused)
99 {
100 sock_set_blocking(s, 1);
101 sock_setcb(s, SCB_READLINE, readfirstline_callback);
102 send_server();
103 }
104
105 /*************************************************************************/
106
107 /* Actions to perform when connection to server is broken. */
108
109 void disconnect_callback(Socket *s, void *param)
110 {
111 /* We are no longer linked */
112 linked = 0;
113
114 if (param == DISCONN_REMOTE || param == DISCONN_CONNFAIL) {
115 int errno_save = errno;
116 const char *msg = (param==DISCONN_REMOTE ? "Read error from server"
117 : "Connection to server failed");
118 snprintf(quitmsg, sizeof(quitmsg),
119 "%s: %s", msg, strerror(errno_save));
120 if (param == DISCONN_REMOTE) {
121 /* If we were already connected, make sure any changed data is
122 * updated before we terminate. */
123 delayed_quit = 1;
124 save_data = 1;
125 } else {
126 /* The connection was never made in the first place, so we
127 * discard any changes (such as expirations) made on the
128 * assumption that either a configuration problem or other
129 * external problem exists. Such changes will be saved the
130 * next time Services successfully connects to a server. */
131 quitting = 1;
132 }
133 }
134 sock_setcb(s, SCB_READLINE, NULL);
135 }
136
137 /*************************************************************************/
138
139 /* Actions to perform when first line is read from socket. */
140
141 void readfirstline_callback(Socket *s, void *param_unused)
142 {
143 sock_setcb(s, SCB_READLINE, readline_callback);
144
145 if (!sgets2(inbuf, sizeof(inbuf), s)) {
146 /* This shouldn't happen, but just in case... */
147 disconn(s);
148 fatal("Unable to read greeting from server socket");
149 }
150 if (strnicmp(inbuf, "ERROR", 5) == 0) {
151 /* Close server socket first to stop wallops, since the other
152 * server doesn't want to listen to us anyway */
153 disconn(s);
154 fatal("Remote server returned: %s", inbuf);
155 }
156
157 /* We're now linked to the network */
158 linked = 1;
159
160 /* Announce a logfile error if there was one */
161 if (openlog_failed) {
162 wallops(NULL, "Warning: couldn't open logfile: %s",
163 strerror(openlog_errno));
164 openlog_failed = 0;
165 }
166
167 /* Bring in our pseudo-clients */
168 introduce_user(NULL);
169
170 /* Let modules do their startup stuff */
171 call_callback(cb_connect);
172
173 /* Process the line we read in above */
174 process();
175 }
176
177 /*************************************************************************/
178
179 /* Actions to perform when subsequent lines are read from socket. */
180
181 void readline_callback(Socket *s, void *param_unused)
182 {
183 if (sgets2(inbuf, sizeof(inbuf), s))
184 process();
185 }
186
187 /*************************************************************************/
188 /*************************************************************************/
189
190 /* Lock the data directory if possible; return nonzero on success, zero on
191 * failure (data directory already locked or cannot create lock file).
192 * On failure, errno will be EEXIST if the directory was already locked or
193 * a value other than EEXIST if an error occurred creating the lock file.
194 *
195 * This does not attempt to correct for NFS brokenness w.r.t. O_EXCL and
196 * will contain a race condition when used on an NFS filesystem (or any
197 * other filesystem which does not support O_EXCL properly).
198 */
199
200 int lock_data(void)
201 {
202 int fd;
203
204 errno = 0;
205 fd = open(LockFilename, O_WRONLY | O_CREAT | O_EXCL, 0);
206 if (fd >= 0) {
207 close(fd);
208 return 1;
209 }
210 return 0;
211 }
212
213 /*************************************************************************/
214
215 /* Check whether the data directory is locked without actually attempting
216 * to lock it. Returns 1 if locked, 0 if not, or -1 if an error occurred
217 * while trying to check (in which case errno will be set to an appropriate
218 * value, i.e. whatever access() returned).
219 */
220
221 int is_data_locked(void)
222 {
223 errno = 0;
224 if (access(LockFilename, F_OK) == 0)
225 return 1;
226 if (errno == ENOENT)
227 return 0;
228 return -1;
229 }
230
231 /*************************************************************************/
232
233 /* Unlock the data directory. Assumes we locked it in the first place.
234 * Returns 1 on success, 0 on failure (unable to remove the lock file), or
235 * -1 if the lock file didn't exist in the first place (possibly because it
236 * was removed by another (misbehaving) program).
237 */
238
239 int unlock_data(void)
240 {
241 errno = 0;
242 if (unlink(LockFilename) == 0)
243 return 1;
244 if (errno == ENOENT)
245 return -1;
246 return 0;
247 }
248
249 /*************************************************************************/
250
251 /* Subroutine to save databases. */
252
253 void save_data_now(void)
254 {
255 if (!lock_data()) {
256 if (errno == EEXIST) {
257 log("warning: databases are locked, not updating");
258 wallops(NULL,
259 "\2Warning:\2 Databases are locked, and cannot be updated."
260 " Remove the `%s%s%s' file to allow database updates.",
261 *LockFilename=='/' ? "" : services_dir,
262 *LockFilename=='/' ? "" : "/", LockFilename);
263 } else {
264 log_perror("warning: unable to lock databases, not updating");
265 wallops(NULL, "\2Warning:\2 Unable to lock databases; databases"
266 " will not be updated.");
267 }
268 call_callback_1(cb_save_complete, 0);
269 } else {
270 log_debug(1, "Saving databases");
271 save_all_dbtables();
272 if (!unlock_data()) {
273 log_perror("warning: unable to unlock databases");
274 wallops(NULL,
275 "\2Warning:\2 Unable to unlock databases; future database"
276 " updates may fail until the `%s%s%s' file is removed.",
277 *LockFilename=='/' ? "" : services_dir,
278 *LockFilename=='/' ? "" : "/", LockFilename);
279 }
280 call_callback_1(cb_save_complete, 1);
281 }
282 }
283
284 /*************************************************************************/
285 /*************************************************************************/
286
287 /* Main routine. (What does it look like? :-) ) */
288
289 int main(int ac, char **av, char **envp)
290 {
291 volatile time_t last_update; /* When did we last update the databases? */
292 volatile uint32 last_check; /* When did we last check timeouts? */
293
294
295 /*** Initialization stuff. ***/
296
297 if (init(ac, av) < 0) {
298 fprintf(stderr, "Initialization failed, exiting.\n");
299 return 1;
300 }
301
302
303 /* Set up timers. */
304 last_send = time(NULL);
305 last_update = time(NULL);
306 last_check = time(NULL);
307
308 /* The signal handler routine will drop back here with quitting != 0
309 * if it gets called. */
310 DO_SIGSETJMP();
311
312
313 /*** Main loop. ***/
314
315 while (!quitting) {
316 time_t now = time(NULL);
317 int32 now_msec = time_msec();
318
319 log_debug(2, "Top of main loop");
320
321 if (!readonly && (save_data || now-last_update >= UpdateTimeout)) {
322 save_data_now();
323 save_data = 0;
324 last_update = now;
325 }
326 if (delayed_quit)
327 break;
328
329 if (sock_isconn(servsock)) {
330 if (PingFrequency && now - last_send >= PingFrequency)
331 send_cmd(NULL, "PING :%s", ServerName);
332 }
333
334 if (now_msec - last_check >= TimeoutCheck) {
335 check_timeouts();
336 last_check = now_msec;
337 }
338
339 check_sockets();
340
341 if (!MergeChannelModes)
342 set_cmode(NULL, NULL); /* flush out any mode changes made */
343 }
344
345
346 /*** Cleanup stuff. ***/
347
348 cleanup();
349
350 /* Check for restart instead of exit */
351 if (restart) {
352 execve(SERVICES_BIN, av, envp);
353 /* If we get here, the exec() failed; override readonly and write a
354 * note to the log file */
355 {
356 int errno_save = errno;
357 open_log();
358 errno = errno_save;
359 }
360 log_perror("Restart failed");
361 close_log();
362 return 1;
363 }
364
365 /* Terminate program */
366 return 0;
367 }
368
369 /*************************************************************************/
370
371 /*
372 * Local variables:
373 * c-file-style: "stroustrup"
374 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
375 * indent-tabs-mode: nil
376 * End:
377 *
378 * vim: expandtab shiftwidth=4:
379 */