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