ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/vendor/ircservices-5.1.24/main.c
Revision: 1171
Committed: Fri Aug 12 20:00:46 2011 UTC (12 years, 7 months ago) by michael
Content type: text/x-csrc
File size: 11634 byte(s)
Log Message:
- Import ircservices-5.1.24. Don't ever think about modifying anything in this
  folder!
  Since Andrew Church has discontinued his services project in April 2011, the
  ircd-hybrid team has been given permissions to officially continue and
  maintain the already mentioned project.
  The name of this project will be changed for the reason being that the current
  name "IRC Services" is way too generic these days.

  Remember: Don't ever modify anything in here. This folder is kept for reference.

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 */