1 /*-
2 * Copyright (c) 2001, 2002, 2004 Lev Walkin <vlm@lionet.info>.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $Id: main.c,v 1.44 2004/09/17 05:37:58 vlm Exp $
27 */
28
29 #include "ipcad.h"
30 #include "cfgvar.h"
31 #include "servers.h"
32 #include "opt.h"
33 #include "pidfile.h"
34 #include "storage.h"
35
36 enum phase {
37 DRAW_CHROOT,
38 DRAW_SETXID,
39 };
40 int draw_restricted_envir(enum phase); /* 0: chroot(), 1: set?id() */
41 void terminate_threads();
42
43 void
sigalarm(int z)44 sigalarm(int z) {
45 signal(z, sigalarm);
46 }
47
48 void
set_display_now(int z)49 set_display_now(int z) {
50 display_now = 1;
51 signal(z, set_display_now);
52 }
53
54 void
sigquit(int z)55 sigquit(int z) {
56 signoff_now = 1;
57 signal(z, sigquit);
58 }
59
60 int save_on_exit = 0;
61
62 double self_started;
63
64 int
main(int ac,char ** av)65 main(int ac, char **av) {
66 struct timeval tv;
67 char *config_file = CONFIG_FILE;
68 int restore = 0;
69 int disable_servers = 0;
70 int pidfile_fd = -1;
71 int devnull_fd = -1;
72 int c;
73 unsigned i;
74
75 gettimeofday(&tv, NULL);
76 self_started = tv.tv_sec + (double)tv.tv_usec / 1000000;
77
78 while((c = getopt(ac, av, "dhc:rsSv")) != -1)
79 switch(c) {
80 case 'c':
81 config_file = optarg;
82 if(!config_file)
83 usage();
84 break;
85 case 'd':
86 daemon_mode = 1;
87 break;
88 case 'h':
89 usage();
90 break;
91 case 'r':
92 restore = 1;
93 break;
94 case 's':
95 save_on_exit = 1;
96 break;
97 case 'S':
98 disable_servers = 1;
99 break;
100 case 'v':
101 fprintf(stderr,
102 IPCAD_VERSION_STRING
103 IPCAD_COPYRIGHT "\n");
104 exit(EX_USAGE);
105 default:
106 usage();
107 }
108
109 if(init_pthread_options()) {
110 fprintf(stderr, "Can't initialize thread options.\n");
111 exit(EX_OSERR);
112 }
113
114 /*
115 * Set defaults.
116 */
117 conf->set_gid = (gid_t)-1;
118 conf->set_uid = (uid_t)-1;
119 conf->rsh_ttl = 3; /* IP TTL for RSH */
120 conf->netflow_version = 5;
121 conf->netflow_timeout_active = 30 * 60; /* Seconds */
122 conf->netflow_timeout_inactive = 15; /* Seconds */
123 conf->netflow_packet_interval = 1; /* packets of packets */
124 for(i = 0; i < sizeof(agr_portmap)/sizeof(agr_portmap[0]); i++)
125 agr_portmap[i] = i; /* Default is 1:1 mapping. */
126
127 /*
128 * Open /dev/null for daemonizing.
129 */
130 devnull_fd = open(_PATH_DEVNULL, O_RDWR, 0);
131 if(devnull_fd == -1) {
132 fprintf(stderr, "Can't open " _PATH_DEVNULL ": %s\n",
133 strerror(errno));
134 exit(EX_OSERR);
135 }
136
137 /*
138 * Read specified or default configuration file.
139 */
140 if(cfgread(config_file))
141 exit(EX_NOINPUT);
142
143 /******************************/
144 /* Process configuration data */
145 /******************************/
146
147 /*
148 * Simple checks
149 */
150 if(conf->packet_sources_head == NULL) {
151 fprintf(stderr, "No interfaces initialized.\n");
152 exit(EX_NOINPUT);
153 }
154
155 if(save_on_exit || restore) {
156 if(conf->dump_table_file == NULL) {
157 fprintf(stderr, "Dump file is not defined.\n");
158 exit(EX_NOINPUT);
159 }
160 }
161
162 /*
163 * Simple deeds.
164 */
165 /* Pre-open certain files before doing chroot(). */
166 if(ifst_preopen())
167 exit(EX_OSERR);
168
169 /* Security */
170 if(draw_restricted_envir(DRAW_CHROOT))
171 /* Function will cry loudly if anything wrong */
172 exit(EX_DATAERR);
173
174 /* PID file */
175 if(conf->pidfile) {
176 pidfile_fd = make_pid_file(conf->pidfile);
177 if(pidfile_fd == -1) {
178 fprintf(stderr,
179 "Can't initialize pid file %s%s%s: %s\n",
180 conf->chroot_to?conf->chroot_to:"",
181 conf->chroot_to?"/":"",
182 conf->pidfile, strerror(errno));
183 if(conf->chroot_to) {
184 fprintf(stderr,
185 "Make sure you have %s under %s "
186 "used as new root. man 2 chroot.\n",
187 dirname(conf->pidfile),
188 conf->chroot_to);
189 }
190 exit(EX_DATAERR);
191 }
192 }
193
194 time(&active_storage.create_time);
195 time(&netflow_storage.create_time);
196
197 /* Restore previously saved table */
198 if(restore) {
199 import_table(conf->dump_table_file, stderr, 1);
200 }
201
202 /* Daemon mode should be entered BEFORE threads created. */
203 if(daemon_mode) {
204 fflush(NULL);
205
206 switch(fork()) {
207 case -1:
208 perror("ipcad");
209 exit(EX_OSERR);
210 case 0:
211 break;
212 default:
213 _exit(0);
214 }
215
216 if(setsid() == -1) {
217 perror("setsid() failed");
218 exit(EX_OSERR);
219 }
220
221 fprintf(stderr, "Daemonized.\n");
222 fflush(stderr); /* As a remainder */
223
224 (void)dup2(devnull_fd, 0);
225 (void)dup2(devnull_fd, 1);
226 (void)dup2(devnull_fd, 2);
227 if(devnull_fd > 2)
228 close(devnull_fd);
229
230 /* Update pid value */
231 if(pidfile_fd != -1) {
232 (void)write_pid_file(pidfile_fd, getpid(), NULL);
233 /* Ignore virtually impossible errors */
234 }
235 }
236
237
238 /***********************************/
239 /* Set appropriate signal handlers */
240 /***********************************/
241
242 /*
243 * Ignore signals, by default
244 */
245 for(c = 1; c < 32; c++) {
246 switch(c) {
247 case SIGCHLD:
248 /*
249 * Setting SIG_IGN breaks threading support
250 * in Linux 2.4.20-20.9
251 */
252 case SIGSEGV:
253 case SIGBUS:
254 /*
255 * No use to continue after SIGSEGV/SIGBUS.
256 * Program must not generate SIGSEGV/SIGBUS
257 * in the first place.
258 */
259 continue;
260 }
261 signal(c, SIG_IGN);
262 }
263
264 signal(SIGALRM, sigalarm);
265 signal(SIGINT, set_display_now);
266 signal(SIGHUP, set_display_now);
267 signal(SIGQUIT, sigquit);
268 signal(SIGPIPE, SIG_IGN);
269 signal(SIGTERM, sigquit);
270 signal(SIGTTIN, SIG_IGN);
271
272 siginterrupt(SIGALRM, 1);
273
274 /*******************************************/
275 /* Start servers to serve clients requests */
276 /*******************************************/
277
278 if(disable_servers == 0) {
279 if( start_servers() != 0 ) {
280 fprintf(stderr,
281 "Failed to start one or more servers.\n");
282 exit(EX_OSERR);
283 }
284 }
285
286
287 #ifdef HAVE_SETPRIORITY
288 /*
289 * Set nice, but safe priority.
290 * Required to process high loads without
291 * significant packet loss.
292 */
293 setpriority(PRIO_PROCESS, 0, -15);
294 #endif
295
296 /* Security */
297 if(draw_restricted_envir(DRAW_SETXID))
298 /* Function will cry loudly if anything wrong */
299 exit(EX_DATAERR);
300
301 /*
302 * The main working loop.
303 */
304 process_packet_sources(conf->packet_sources_head);
305
306 /* Termination */
307 terminate_threads();
308
309 if(save_on_exit)
310 make_dump(conf->dump_table_file, stderr);
311
312 /* Leave the pid file gracefully */
313 if(pidfile_fd != -1) {
314 int fd = pidfile_fd;
315 pidfile_fd = -1;
316 /* Empty the .pid file */
317 (void)write_pid_file(fd, 0, "");
318 close(fd);
319 }
320
321 fprintf(stderr, "Quit.\n");
322
323 return 0;
324 }
325
326 /*
327 * Block signals that must be handled by the main thread only.
328 */
329 int
block_certain_signals(sigset_t * old_set)330 block_certain_signals(sigset_t *old_set) {
331 sigset_t set;
332 sigemptyset(&set);
333 sigaddset(&set, SIGINT);
334 sigaddset(&set, SIGHUP);
335 sigaddset(&set, SIGQUIT);
336 sigaddset(&set, SIGTERM);
337 return sigprocmask(SIG_BLOCK, &set, old_set);
338 }
339
340 int
draw_restricted_envir(enum phase phase)341 draw_restricted_envir(enum phase phase) {
342
343 if(phase == DRAW_CHROOT) {
344
345 if(conf->chroot_to == NULL)
346 /* We were not told to do chroot() */
347 return 0;
348
349 if(conf->chroot_to[0] != '/')
350 /* Chroot directory is not an absolute pathname */
351 return -1;
352
353 if(chroot(conf->chroot_to) != 0) {
354 fprintf(stderr, "Can't do chroot(%s): %s\n",
355 conf->chroot_to, strerror(errno));
356 return -1;
357 }
358
359 if(chdir("/") != 0) {
360 perror("Can't do chdir(/)");
361 return -1;
362 }
363
364 } else if(phase == DRAW_SETXID) {
365 if(conf->set_gid != (gid_t)-1) {
366 if(setgid(conf->set_gid) == -1) {
367 perror("Can't do setgid()");
368 return -1;
369 }
370 }
371
372 if(conf->set_uid != (uid_t)-1) {
373 if(setuid(conf->set_uid) == -1) {
374 perror("Can't do setuid()");
375 return -1;
376 }
377 }
378 }
379
380 return 0;
381 }
382
383
384 void
terminate_threads()385 terminate_threads() {
386 packet_source_t *ps;
387
388 end_servers();
389
390 printf("Signalling interface threads");
391 for(ps = conf->packet_sources_head; ps; ps = ps->next) {
392 if(!ps->thid)
393 continue;
394
395 printf("."); fflush(stdout);
396
397 #ifdef HAVE_PTHREAD_CANCEL
398 /*
399 * When libpcap is being used, the dispatcher function
400 * does not return when -1/EINTR is received from the
401 * capture device socket. It just retries to read,
402 * blocking the whole process.
403 * This pthread_cancel() ensures that the thread will
404 * be killed doing such a nasty loop.
405 */
406 pthread_cancel(ps->thid);
407 #endif
408
409 /* Notify the process */
410 pthread_kill(ps->thid, SIGALRM);
411 }
412
413 printf("\nWaiting for interface processing threads to terminate...\n");
414
415 for(ps = conf->packet_sources_head; ps; ps = ps->next) {
416 if(!ps->thid)
417 continue;
418
419 if(pthread_join(ps->thid, NULL)) {
420 if(errno == EINVAL)
421 printf("Thread processing %s "
422 "is not a joinable thread.\n",
423 IFNameBySource(ps));
424 if(errno == ESRCH)
425 printf("No thread running "
426 "for processing %s\n",
427 IFNameBySource(ps));
428 if(errno == EDEADLK) /* Impossible state */
429 printf("Deadlock avoided "
430 "for thread processing %s\n",
431 IFNameBySource(ps));
432 } else {
433 printf("Thread processing %s terminated.\n",
434 IFNameBySource(ps));
435 }
436 }
437 }
438
439
440 /*
441 * Code for people
442
443 ���:
444 - ���� ����� � ���� ������.
445 ����:
446 - �������.
447 - �� ����� ������.
448 - �� ������� �� ����!
449 - ��, � ��� ����� ���� �� ����� ��������.
450 - �� ����� ������, � �� ��������� � ��� ����� �� �����.
451
452 */
453