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