1 /*
2  *	binkleyforce -- unix FTN mailer project
3  *
4  *	Copyright (c) 1998-2000 Alexander Belkin, 2:5020/1398.11
5  *
6  *	This program is free software; you can redistribute it and/or modify
7  *	it under the terms of the GNU General Public License as published by
8  *	the Free Software Foundation; either version 2 of the License, or
9  *	(at your option) any later version.
10  *
11  *	$Id: bforce.c,v 1.1.1.1 2004/09/09 09:52:37 kstepanenkov Exp $
12  */
13 
14 #include "includes.h"
15 #include "confread.h"
16 #include "version.h"
17 #include "logger.h"
18 #include "util.h"
19 #include "bforce.h"
20 #include "nodelist.h"
21 #include "session.h"
22 
23 /* PID of our child process (if use fork) */
24 pid_t child_pid = 0;
25 
26 const char *BFERR[] = {
27 	/* 00 */ "Successfull",
28 	/* 01 */ "Fatal error occured",
29 	/* 02 */ "Unknown phone number(or ip address)",
30 	/* 03 */ "Modem port busy (or open error)",
31 	/* 04 */ "System locked now",
32 	/* 05 */ "Try later",
33 	/* 06 */ "Modem not response",
34 	/* 07 */ "Not working now",
35 	/* 08 */ "Unused entry",
36 	/* 09 */ "Unused entry",
37 	/* 10 */ "Can't connect to remote",
38 	/* 11 */ "Can't connect to remote: busy",
39 	/* 12 */ "Can't connect to remote: nocarrier",
40 	/* 13 */ "Can't connect to remote: nodialtone",
41 	/* 14 */ "Can't connect to remote: noanswer",
42 	/* 15 */ "Can't connect to remote: error",
43 	/* 16 */ "Can't connect to remote: user defined 16",
44 	/* 17 */ "Can't connect to remote: user defined 17",
45 	/* 18 */ "Can't connect to remote: user defined 18",
46 	/* 19 */ "Can't connect to remote: user defined 19",
47 	/* 20 */ "Connect speed too low",
48 	/* 21 */ "Cannot handshake with remote",
49 	/* 22 */ "Xmiting error",
50 	/* 23 */ "CPS too low",
51 	/* 24 */ "Reached stop time",
52 	/* 25 */ "Unused entry",
53 	/* 26 */ "Unused entry",
54 	/* 27 */ "Unused entry"
55 };
56 
57 static void deinit_opts(s_bforce_opts *opts);
58 
59 /*
60 static void print_compiled_configuration(void)
61 {
62 	printf("BFORCE_LOGFILE = "BFORCE_LOGFILE"\n");
63 	printf("BFORCE_DEBFILE = "BFORCE_DEBFILE"\n");
64 	printf("DAEMON_LOGFILE = "DAEMON_LOGFILE"\n");
65 	printf("BFORCE_CFGFILE = "BFORCE_CFGFILE"\n");
66 	printf("BF_OS = "BF_OS"\n");
67 }
68 */
69 
usage(void)70 static void usage(void)
71 {
72 	printf_usage(NULL,
73 		"usage: bforce [-fmh] [-I<include>] [-n<phone>] [-l<line_number>]\n"
74 		"              [-a<ip_address>] [-S<connect>] [-p<device>] <node>\n"
75 		"       bforce [-ih] [-I<include>] [-S<connect>]\n"
76 		"              <tsync|yoohoo|emsi|binkp|auto> (this implies slave mode)\n"
77 		"       bforce [-dh] [-C<config>] [-I<include>]\n"
78 		"\n"
79 		"options:\n"
80 		"  -d                run as daemon\n"
81 		"  -q                terminate daemon\n"
82 		"  -i                run from inetd (for slave mode only)\n"
83 		"  -f                ignore system's work time\n"
84 		"  -o                starts outgoing session on stdin/stdout\n"
85 		"  -C <config>       main config file name (\"%s\")\n"
86 		"  -I <config>       additional config file name\n"
87 		"  -n <phone>        override phone number\n"
88 		"  -l <line_number>  call on this hidden line (default is 0) \n"
89 		"  -a <ip_address>   override internet address\n"
90 		"  -S <connect_str>  connect string (for slave mode only)\n"
91 		"  -p <port>         override modem port (must be defined in config)\n"
92 		"  -h                show this help message\n"
93 		"\n",
94 		conf_getconfname()
95 	);
96 }
97 
bforce_create_dirs(void)98 int bforce_create_dirs(void)
99 {
100 	const char *p_dirname = conf_string(cf_status_directory);
101 
102 	if( p_dirname )
103 	{
104 		if( access(p_dirname, F_OK) == -1 || !is_directory(p_dirname) )
105 		{
106 			if( directory_create(p_dirname, 0700) == -1 )
107 			{
108 				logerr("cannot create spool directory \"%s\"", p_dirname);
109 				return -1;
110 			}
111 		}
112 	}
113 
114 	return 0;
115 }
116 
117 /*
118  *  Universal signal handler for parent processes :) Will resend
119  *  signals to child process
120  */
parent_sighandler(int sig)121 static RETSIGTYPE parent_sighandler(int sig)
122 {
123 	/* Send this signal to our child */
124 	if( child_pid ) kill(child_pid, sig);
125 }
126 
127 /*
128  *  Return non-zero value to indicate error, on success - return zero
129  *  to child process and NEVER return to parent process
130  */
usefork(void)131 static int usefork(void)
132 {
133 	pid_t cpid;	/* child PID */
134 	pid_t wpid;	/* waitpid() returned value */
135 	int status = 0;
136 
137 	cpid = fork();
138 
139 	if( cpid == 0 )
140 		{ return 0; }
141 
142 	if( cpid < 0 )
143 		{ logerr("failed fork() call"); return 1; }
144 
145 	/*
146 	 *  Now we are parent process
147 	 */
148 
149 	/* Put PID into global variable */
150 	child_pid = cpid;
151 
152 	/* Setup signals handling */
153 	signal(SIGHUP,  parent_sighandler);
154 	signal(SIGINT,  parent_sighandler);
155 	signal(SIGTERM, parent_sighandler);
156 	signal(SIGUSR1, parent_sighandler);
157 	signal(SIGUSR2, parent_sighandler);
158 
159 	/* Wait until child die */
160 	while( (wpid = waitpid(cpid, &status, 0)) < 0 && errno == EINTR )
161 	{
162 		/* EMPTY LOOP */
163 	}
164 
165 	if( wpid == cpid && WIFEXITED(status) )
166 	{
167 		/* Child was terminated with _exit() */
168 		exit(WEXITSTATUS(status));
169 	}
170 	else if( wpid < 0 && WIFSIGNALED(status) )
171 	{
172 		/* Child was terminated by signal */
173 		kill(getpid(), WTERMSIG(status));
174 	}
175 	exit(BFERR_FATALERROR);
176 }
177 
bforce_master(const s_bforce_opts * opts)178 static int bforce_master(const s_bforce_opts *opts)
179 {
180 	s_falist *tmpl;
181 	int rc, maxrc = BFERR_NOERROR;
182 
183 #ifdef GETPGRP_VOID
184 	if( getpgrp() == getpid() )
185 #else
186 	if( getpgrp(0) == getpid() )
187 #endif
188 	{
189 		/* We are process group leader -> fork() */
190 		if( usefork() ) return BFERR_FATALERROR;
191 	}
192 
193 	for( tmpl = opts->addrlist; tmpl; tmpl = tmpl->next )
194 	{
195 		int callopt = 0;
196 
197 		if( opts->iaddr ) callopt |= CALLOPT_INET;
198 		if( opts->force ) callopt |= CALLOPT_FORCE;
199 
200 		rc = call_system(tmpl->addr, opts);
201 
202 		if( rc > maxrc ) maxrc = rc;
203 	}
204 
205 	return maxrc;
206 }
207 
bforce_slave(const s_bforce_opts * opts)208 static int bforce_slave(const s_bforce_opts *opts)
209 {
210 	return answ_system(opts->stype, opts->connect, opts->inetd);
211 }
212 
bforce_daemon(const s_bforce_opts * opts)213 static int bforce_daemon(const s_bforce_opts *opts)
214 {
215 	int forkrc = fork();
216 
217 	if( forkrc == -1 )
218         {
219 		logerr("cannot run daemon: failed fork() call");
220 		return BFERR_FATALERROR;
221 	}
222 	else if( forkrc > 0 )
223 	{
224 		return BFERR_NOERROR;
225 	}
226 
227 	/*
228 	 * We are inside a child process.
229 	 * Create new session and run daemon
230 	 */
231 	setsid();
232 
233 	return daemon_run(opts->confname, opts->incname, opts->quit);
234 }
235 
main(int argc,char * argv[],char * envp[])236 int main(int argc, char *argv[], char *envp[])
237 {
238 	s_bforce_opts opts;
239 	int rc = 0;
240 	int ch = 0;
241 	int role = 0;
242 
243 	memset(&opts, '\0', sizeof(s_bforce_opts));
244 
245 	while( (ch=getopt(argc, argv, "hodqr:ifC:I:n:l:a:S:p:")) != EOF )
246 	{
247 		switch( ch ) {
248 		case 'h':
249 			usage();
250 			exit(BFERR_NOERROR);
251 		case 'd':
252 			if( opts.inetd || opts.force || opts.phone
253 			 || opts.hiddline || opts.iaddr || opts.connect
254 			 || opts.device || opts.quit )
255 				{ usage(); exit(BFERR_FATALERROR); }
256 			else
257 				{ opts.daemon = 1; }
258 			break;
259 		case 'q':
260 			if( opts.inetd || opts.force || opts.phone
261 			 || opts.hiddline || opts.iaddr || opts.connect
262 			 || opts.device || opts.daemon )
263 				{ usage(); exit(BFERR_FATALERROR); }
264 			else
265 				{ opts.daemon = 1; opts.quit = 1; }
266 			break;
267 		case 'i':
268 			if( opts.daemon )
269 				{ usage(); exit(BFERR_FATALERROR); }
270 			else
271 				{ opts.inetd = 1; }
272 			break;
273 		case 'f':
274 			if( opts.daemon )
275 				{ usage(); exit(BFERR_FATALERROR); }
276 			else
277 				{ opts.force = 1; }
278 			break;
279 		case 'o':
280 			if( opts.dontcall )
281 				{ usage(); exit(BFERR_FATALERROR); }
282 			else
283 				{ opts.dontcall = TRUE; }
284 			break;
285 		case 'C':
286 			if( opts.confname ) free(opts.confname);
287 			if( optarg ) opts.confname = (char *)xstrcpy(optarg);
288 			break;
289 		case 'I':
290 			if( opts.incname ) free(opts.incname);
291 			if( optarg ) opts.incname = (char *)xstrcpy(optarg);
292 			break;
293 		case 'n':
294 			if( opts.daemon )
295 				{ usage(); exit(BFERR_FATALERROR); }
296 			else
297 			{
298 				if( opts.phone ) free(opts.phone);
299 				if( optarg ) opts.phone = (char *)xstrcpy(optarg);
300 			}
301 			break;
302 		case 'l':
303 			if( ISDEC(optarg) && opts.daemon == 0 )
304 				opts.hiddline = atoi(optarg);
305 			else
306 				{ usage(); exit(BFERR_FATALERROR); }
307 			break;
308 		case 'a':
309 			if( opts.daemon )
310 				{ usage(); exit(BFERR_FATALERROR); }
311 			else
312 			{
313 				if( opts.iaddr ) free(opts.iaddr);
314 				if( optarg ) opts.iaddr = (char *)xstrcpy(optarg);
315 			}
316 			break;
317 		case 'S':
318 			if( opts.daemon )
319 				{ usage(); exit(BFERR_FATALERROR); }
320 			else
321 			{
322 				if( opts.connect ) free(opts.connect);
323 				if( optarg ) opts.connect = (char *)xstrcpy(optarg);
324 			}
325 			break;
326 		case 'p':
327 			if( opts.daemon )
328 				{ usage(); exit(BFERR_FATALERROR); }
329 			else
330 			{
331 				if( opts.device ) free(opts.device);
332 				if( optarg ) opts.device = (char *)xstrcpy(optarg);
333 			}
334 			break;
335 		default :
336 			usage();
337 			exit(BFERR_FATALERROR);
338 		}
339 	}
340 
341 	/* Expression checker use it, so init first */
342 	init_state(&state);
343 
344 	/* Set space available for process title */
345 #ifndef HAVE_SETPROCTITLE
346 	setargspace(argv, envp);
347 #endif
348 
349 	/* Initialise random number generation */
350 	(void)srand((unsigned)time(NULL));
351 
352 	/* Initialise current locale */
353 	(void)setlocale(LC_ALL, "");
354 
355 	/* Set secure process umask */
356 	(void)umask(~(S_IRUSR|S_IWUSR));
357 
358 	/* Now process non-option arguments */
359 	if( opts.daemon == FALSE )
360 	{
361 		const char *p;
362 		s_faddr addr;
363 		s_falist **alist = &opts.addrlist;
364 
365 		while( (optind < argc) && (p = argv[optind++]) )
366 		{
367 			for( ; *p == '*'; p++ );
368 
369 			if( strcasecmp(p, "tsync") == 0 )
370 			{
371 				role = 0;
372 				opts.stype = SESSION_FTSC;
373 			}
374 			else if( strcasecmp(p, "yoohoo") == 0 )
375 			{
376 				role = 0;
377 				opts.stype = SESSION_YOOHOO;
378 			}
379 			else if( strncasecmp(p, "emsi", 4) == 0 )
380 			{
381 				role = 0;
382 				opts.stype = SESSION_EMSI;
383 			}
384 			else if( strcasecmp(p, "binkp") == 0 )
385 			{
386 				role = 0;
387 				opts.stype = SESSION_BINKP;
388 			}
389 			else if( strcasecmp(p, "auto") == 0 )
390 			{
391 				role = 0;
392 				opts.stype = SESSION_UNKNOWN;
393 			}
394 			else if( ftn_addrparse(&addr, p, FALSE) == 0 )
395 			{
396 				role = 1;
397 				(*alist) = (s_falist*)xmalloc(sizeof(s_falist));
398 				memset(*alist, '\0', sizeof(s_falist));
399 				(*alist)->addr = addr;
400 				alist = &(*alist)->next;
401 			}
402 			else
403 			{
404 				printf("invalid address \"%s\"", p);
405 				usage();
406 				exit(BFERR_FATALERROR);
407 			}
408 		}
409 
410 		if( opts.dontcall && role == 0 )
411 		{
412 			usage();
413 			exit(BFERR_FATALERROR);
414 		}
415 	}
416 
417 /*	if( (rc = log_open(log_getfilename(LOG_FILE_SESSION), NULL, NULL)) )
418 	{
419 		bf_log("can't continue without logging");
420 		gotoexit(BFERR_FATALERROR);
421 	}
422 */
423 	/* Process primary config file */
424 	if( opts.confname && *opts.confname )
425 		rc = conf_readconf(opts.confname, 0);
426 	else
427 		rc = conf_readconf(conf_getconfname(), 0);
428 
429 	if( rc ) gotoexit(BFERR_FATALERROR);
430 
431 	/* Process additional config file, ignore errors */
432 	if( opts.incname && *opts.incname )
433 		(void)conf_readconf(opts.incname, 1);
434 
435 	/* Reopen log file if it was defined in config */
436 	if( log_open(log_getfilename(LOG_FILE_SESSION), NULL, NULL) )
437 	{
438 		bf_log("can't continue without logging");
439 		gotoexit(BFERR_FATALERROR);
440 	}
441 
442 #ifdef DEBUG
443 	/* Same for the debug file */
444 	(void)debug_setfilename(log_getfilename(LOG_FILE_DEBUG));
445 #endif
446 
447 	if( opts.daemon )
448 		rc = bforce_daemon(&opts);
449 	else if( role )
450 		rc = bforce_master(&opts);
451 	else
452 		rc = bforce_slave(&opts);
453 
454 exit:
455 
456 	deinit_conf();
457 	deinit_opts(&opts);
458 
459 	/* Shutdown logging services */
460 	if( log_isopened() ) log_close();
461 #ifdef DEBUG
462 	if( debug_isopened() ) debug_close();
463 #endif
464 
465 	exit(rc);
466 }
467 
deinit_opts(s_bforce_opts * opts)468 static void deinit_opts(s_bforce_opts *opts)
469 {
470 	if( opts->confname ) free(opts->confname);
471 	if( opts->incname  ) free(opts->incname);
472 	if( opts->phone    ) free(opts->phone);
473 	if( opts->iaddr    ) free(opts->iaddr);
474 	if( opts->connect  ) free(opts->connect);
475 	if( opts->device   ) free(opts->device);
476 	if( opts->addrlist ) deinit_falist(opts->addrlist);
477 
478 	memset(opts, '\0', sizeof(s_bforce_opts));
479 }
480 
481