603 lines
13 KiB
C
603 lines
13 KiB
C
/*
|
|
** $Id$
|
|
**
|
|
** Copyright 1997-1998 by Michael Herold <michael@abadonna.mayn.de>
|
|
**
|
|
** $Log$
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <getopt.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <limits.h>
|
|
|
|
#include "config.h"
|
|
#include "log.h"
|
|
#include "tcl.h"
|
|
#include "modem.h"
|
|
#include "rc.h"
|
|
#include "voice.h"
|
|
#include "stringutils.h"
|
|
#include "tclscript.h"
|
|
#include "vboxgetty.h"
|
|
|
|
/** Variables ************************************************************/
|
|
|
|
static char *progbasename;
|
|
|
|
char temppathname[PATH_MAX + 1];
|
|
|
|
/** Structures ***********************************************************/
|
|
|
|
static struct vboxrc rc_getty_c[] =
|
|
{
|
|
{ "init" , NULL },
|
|
{ "initnumber" , NULL },
|
|
{ "badinitsexit" , NULL },
|
|
{ "initpause" , NULL },
|
|
{ "commandtimeout", NULL },
|
|
{ "echotimeout" , NULL },
|
|
{ "ringtimeout" , NULL },
|
|
{ "alivetimeout" , NULL },
|
|
{ NULL , NULL }
|
|
};
|
|
|
|
static struct option arguments[] =
|
|
{
|
|
{ "version" , no_argument , NULL, 'v' },
|
|
{ "help" , no_argument , NULL, 'h' },
|
|
{ "debug" , required_argument, NULL, 'x' },
|
|
{ "device" , required_argument, NULL, 'd' },
|
|
{ NULL , 0 , NULL, 0 }
|
|
};
|
|
|
|
struct vboxmodem vboxmodem;
|
|
|
|
/** Prototypes ***********************************************************/
|
|
|
|
static int parse_getty_rc(unsigned char *);
|
|
static void show_usage(int, int);
|
|
static int process_incoming_call(void);
|
|
static int run_modem_init(void);
|
|
static int parse_user_rc(struct vboxincomingcall *);
|
|
|
|
|
|
/*************************************************************************/
|
|
/** The magic main... **/
|
|
/*************************************************************************/
|
|
|
|
void main(int argc, char **argv)
|
|
{
|
|
char *isdnttyname;
|
|
char *stop;
|
|
int opts;
|
|
char *debugstr;
|
|
int debuglvl;
|
|
int i;
|
|
int modemstate;
|
|
int modeminits;
|
|
|
|
progbasename = argv[0];
|
|
|
|
if ((stop = rindex(argv[0], '/'))) progbasename = ++stop;
|
|
|
|
/* Parse command line arguments and set the selected (or default) */
|
|
/* debuglevel. */
|
|
|
|
debugstr = NULL;
|
|
isdnttyname = NULL;
|
|
|
|
while ((opts = getopt_long(argc, argv, "vhx:d:", arguments, (int *)0)) != EOF)
|
|
{
|
|
switch (opts)
|
|
{
|
|
case 'x':
|
|
debugstr = optarg;
|
|
break;
|
|
|
|
case 'd':
|
|
isdnttyname = optarg;
|
|
break;
|
|
|
|
case 'v':
|
|
show_usage(200, 0);
|
|
break;
|
|
|
|
case 'h':
|
|
default:
|
|
show_usage(200, 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (debugstr)
|
|
{
|
|
if (strcasecmp(debugstr, "FULL") != 0)
|
|
{
|
|
debuglvl = LOG_E;
|
|
|
|
for (i = 0; i < strlen(debugstr); i++)
|
|
{
|
|
switch (debugstr[i])
|
|
{
|
|
case 'W':
|
|
case 'w':
|
|
debuglvl |= LOG_W;
|
|
break;
|
|
|
|
case 'I':
|
|
debuglvl |= LOG_I;
|
|
break;
|
|
|
|
case 'A':
|
|
debuglvl |= LOG_A;
|
|
break;
|
|
|
|
case 'D':
|
|
debuglvl |= LOG_D;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else debuglvl = LOG_X;
|
|
|
|
log_set_debuglevel(debuglvl);
|
|
}
|
|
|
|
/* Remove all before the last '/' from the tty name. And check if */
|
|
/* the device is accessable (not really needed since we need root */
|
|
/* privilegs to start). */
|
|
|
|
if (isdnttyname)
|
|
{
|
|
if ((stop = rindex(isdnttyname, '/'))) isdnttyname = ++stop;
|
|
|
|
printstring(temppathname, "/dev/%s", isdnttyname);
|
|
|
|
if (access(temppathname, F_OK|R_OK|W_OK) != 0)
|
|
{
|
|
fprintf(stderr, "\n%s: error: \"%s\" doesn't exist or is not accessable!\n\n", progbasename, temppathname);
|
|
|
|
quit_program(100);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "\n%s: error: isdn tty name is required!\n", progbasename);
|
|
|
|
show_usage(100, 1);
|
|
}
|
|
|
|
/* Check if we start with root privilegs. The permissions will be */
|
|
/* dropped later, but we need root privilegs to open tty's, logs */
|
|
/* etc. */
|
|
|
|
if (getuid() != 0)
|
|
{
|
|
fprintf(stderr, "\n%s: error: need root privilegs to start!\n\n", progbasename);
|
|
|
|
quit_program(100);
|
|
}
|
|
|
|
/* Now its time to open the log. The name of the current tty will */
|
|
/* be appended to the name. */
|
|
|
|
printstring(temppathname, "%s/vboxgetty-%s.log", VBOX_LOGDIR, isdnttyname);
|
|
|
|
log_open(temppathname);
|
|
|
|
/* Start and initialize the tcl-interpreter (version 8.0 or */
|
|
/* higher is required). */
|
|
|
|
if (scr_create_interpreter() == -1)
|
|
{
|
|
log_line(LOG_E, "Can't create/initialize the tcl interpreter!\n");
|
|
|
|
quit_program(100);
|
|
}
|
|
|
|
log_line(LOG_I, "Running vbox version %s (with tcl version %s).\n", VERSION, scr_tcl_version());
|
|
|
|
/* Read the vboxgetty runtime configuration. 1st the global */
|
|
/* and 2nd the tty. */
|
|
|
|
if (parse_getty_rc(isdnttyname) == -1)
|
|
{
|
|
log_line(LOG_E, "Unable to read/parse configuration!\n");
|
|
|
|
quit_program(100);
|
|
}
|
|
|
|
/* Open modem device and do the main loop (initialize, wait, */
|
|
/* answer and alive check). */
|
|
|
|
printstring(temppathname, "/dev/%s", isdnttyname);
|
|
|
|
log_line(LOG_D, "Opening modem device \"%s\" (57600, CTS/RTS)...\n", temppathname);
|
|
|
|
if (vboxmodem_open(&vboxmodem, temppathname) == -1)
|
|
{
|
|
log_line(LOG_E, "Can't open/setup modem device (%s).\n", vboxmodem_error());
|
|
|
|
quit_program(100);
|
|
}
|
|
|
|
signal(SIGINT , quit_program);
|
|
signal(SIGTERM, quit_program);
|
|
|
|
modemstate = VBOXMODEM_STAT_INIT;
|
|
modeminits = 0;
|
|
|
|
while (modemstate != VBOXMODEM_STAT_EXIT)
|
|
{
|
|
switch (modemstate)
|
|
{
|
|
case VBOXMODEM_STAT_INIT:
|
|
|
|
if (run_modem_init() == -1)
|
|
{
|
|
if ((i = (int)xstrtol(rc_get_entry(rc_getty_c, "badinitsexit"), 10)) > 0)
|
|
{
|
|
modeminits++;
|
|
|
|
if (modeminits >= i)
|
|
{
|
|
modemstate = VBOXMODEM_STAT_EXIT;
|
|
modeminits = 0;
|
|
|
|
log_line(LOG_E, "Exit program while bad init limit are reached.\n");
|
|
}
|
|
else log_line(LOG_W, "Bad initialization - Program will exist on %d trys!\n", (i - modeminits));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
modemstate = VBOXMODEM_STAT_WAIT;
|
|
modeminits = 0;
|
|
}
|
|
break;
|
|
|
|
case VBOXMODEM_STAT_WAIT:
|
|
|
|
modem_flush(&vboxmodem, 0);
|
|
|
|
if (modem_wait(&vboxmodem) == 0)
|
|
{
|
|
modemstate = VBOXMODEM_STAT_RING;
|
|
modeminits = 0;
|
|
}
|
|
else modemstate = VBOXMODEM_STAT_TEST;
|
|
|
|
break;
|
|
|
|
case VBOXMODEM_STAT_TEST:
|
|
|
|
log_line(LOG_D, "Checking if modem is still alive...\n");
|
|
|
|
if (modem_command(&vboxmodem, "AT", "OK") > 0)
|
|
{
|
|
modemstate = VBOXMODEM_STAT_WAIT;
|
|
modeminits = 0;
|
|
}
|
|
else modemstate = VBOXMODEM_STAT_INIT;
|
|
|
|
break;
|
|
|
|
case VBOXMODEM_STAT_RING:
|
|
|
|
modem_set_nocarrier(&vboxmodem, 0);
|
|
process_incoming_call();
|
|
modem_hangup(&vboxmodem);
|
|
|
|
modemstate = VBOXMODEM_STAT_INIT;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
log_line(LOG_E, "Unknown modem status %d!\n", modemstate);
|
|
|
|
modemstate = VBOXMODEM_STAT_INIT;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
quit_program(0);
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/** quit_program(): Frees all used resources and exist. **/
|
|
/*************************************************************************/
|
|
/** => rc Exit return code (1-99 reserved for signals) **/
|
|
/*************************************************************************/
|
|
|
|
void quit_program(int rc)
|
|
{
|
|
modem_hangup(&vboxmodem);
|
|
vboxmodem_close(&vboxmodem);
|
|
|
|
scr_remove_interpreter();
|
|
|
|
rc_free(rc_getty_c);
|
|
|
|
log_close();
|
|
|
|
exit(rc);
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/** show_usage(): Shows usage/version message. **/
|
|
/*************************************************************************/
|
|
/** => rc Exit return level (1-99 reserved for signals) **/
|
|
/** => help 1 shows help message, 0 version string **/
|
|
/*************************************************************************/
|
|
|
|
static void show_usage(int rc, int help)
|
|
{
|
|
if (help)
|
|
{
|
|
fprintf(stdout, "\n");
|
|
fprintf(stdout, "Usage: %s [OPTION] [OPTION] [...]\n", progbasename);
|
|
fprintf(stdout, "\n");
|
|
fprintf(stdout, "--device TTY Name of the isdn tty to use (required).\n");
|
|
fprintf(stdout, "--debug CODE Sets debug level (default \"EWI\").\n");
|
|
fprintf(stdout, "--version Display version and exit.\n");
|
|
fprintf(stdout, "--help Display this help and exit.\n");
|
|
fprintf(stdout, "\n");
|
|
}
|
|
else
|
|
{
|
|
fprintf(stdout, "%s version %s\n", progbasename, VERSION);
|
|
}
|
|
|
|
exit(rc);
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/** run_modem_init(): Starts the tcl script to initialize the modem. **/
|
|
/*************************************************************************/
|
|
/** <= 0 on success or -1 on error **/
|
|
/*************************************************************************/
|
|
|
|
static int run_modem_init(void)
|
|
{
|
|
struct vbox_tcl_variable vars[] =
|
|
{
|
|
{ "vbxv_init" , rc_get_entry(rc_getty_c, "init" ) },
|
|
{ "vbxv_initnumber" , rc_get_entry(rc_getty_c, "initnumber") },
|
|
{ NULL , NULL }
|
|
};
|
|
|
|
log_line(LOG_A, "Initializing modem...\n");
|
|
|
|
if (scr_init_variables(vars) == 0)
|
|
{
|
|
if (scr_execute("initmodem.tcl", NULL) == 0) return(0);
|
|
}
|
|
|
|
log_line(LOG_E, "Can't initialize modem!\n");
|
|
|
|
return(-1);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int parse_getty_rc(unsigned char *tty)
|
|
{
|
|
char *name;
|
|
|
|
log_line(LOG_A, "Reading configuration...\n");
|
|
|
|
name = "/usr/local/etc/vboxgetty.conf";
|
|
|
|
if (rc_read(rc_getty_c, name, NULL) < 0)
|
|
{
|
|
if (errno != ENOENT)
|
|
{
|
|
log_line(LOG_E, "Can't open \"%s\" (%s)!\n", name, strerror(errno));
|
|
|
|
return(-1);
|
|
}
|
|
}
|
|
|
|
name = "/usr/local/etc/vboxgetty.conf.ttyI0";
|
|
|
|
if (rc_read(rc_getty_c, name, NULL) < 0)
|
|
{
|
|
if (errno != ENOENT)
|
|
{
|
|
log_line(LOG_E, "Can't open \"%s\" (%s)!\n", name, strerror(errno));
|
|
|
|
return(-1);
|
|
}
|
|
}
|
|
|
|
log_line(LOG_D, "Filling unset configuration variables with defaults...\n");
|
|
|
|
if (!rc_set_empty(rc_getty_c, "init" , "ATZ&B512")) return(-1);
|
|
if (!rc_set_empty(rc_getty_c, "badinitsexit" , "10" )) return(-1);
|
|
if (!rc_set_empty(rc_getty_c, "initpause" , "2500" )) return(-1);
|
|
if (!rc_set_empty(rc_getty_c, "commandtimeout" , "4" )) return(-1);
|
|
if (!rc_set_empty(rc_getty_c, "echotimeout" , "4" )) return(-1);
|
|
if (!rc_set_empty(rc_getty_c, "ringtimeout" , "6" )) return(-1);
|
|
if (!rc_set_empty(rc_getty_c, "alivetimeout" , "1800" )) return(-1);
|
|
|
|
if (!rc_get_entry(rc_getty_c, "initnumber"))
|
|
{
|
|
log_line(LOG_E, "Variable \"initnumber\" *must* be set!\n");
|
|
|
|
return(-1);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/** **/
|
|
/*************************************************************************/
|
|
|
|
static int process_incoming_call(void)
|
|
{
|
|
struct vboxuser vboxuser;
|
|
|
|
char line[VBOXMODEM_BUFFER_SIZE + 1];
|
|
int haverings;
|
|
int waitrings;
|
|
int havesetup;
|
|
|
|
haverings = 0;
|
|
waitrings = 0;
|
|
havesetup = 0;
|
|
|
|
while (modem_read(&vboxmodem, line, (int)xstrtol(rc_get_entry(rc_getty_c, "ringtimeout"), 6)) == 0)
|
|
{
|
|
if ((strncmp(line, "CALLER NUMBER: ", 15) == 0) && (!havesetup))
|
|
{
|
|
xstrncpy(vboxuser.incomingid, &line[15], VBOX_CALL_ID);
|
|
xstrncpy(vboxuser.localphone, "9317840513", VBOX_CALL_NUMBER);
|
|
|
|
if (parse_user_rc(&vboxuser) == 0)
|
|
{
|
|
if ((vboxuser.uid == 0) || (vboxuser.gid == 0))
|
|
{
|
|
log_line(LOG_W, "No user for ID %s found - call will be ignored!\n", vboxcall.callerid);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
havesetup = 1;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (strcmp(line, "RING") == 0)
|
|
{
|
|
haverings++;
|
|
|
|
if (havesetup)
|
|
log_line(LOG_A, "RING #%03d (%s)...\n", haverings, incomingid);
|
|
else
|
|
log_line(LOG_A, "RING #%03d...\n", haverings);
|
|
}
|
|
else
|
|
{
|
|
log_line(LOG_D, "Got junk line \"");
|
|
log_code(LOG_D, line);
|
|
log_text(LOG_D, "\"...\n");
|
|
}
|
|
}
|
|
|
|
return(-1);
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/** **/
|
|
/*************************************************************************/
|
|
|
|
static int parse_user_rc(struct vboxuser *vboxuser)
|
|
{
|
|
char line[VBOX_RCLINE_SIZE + 1];
|
|
FILE *rc;
|
|
char *stop;
|
|
int linenr;
|
|
|
|
log_line(LOG_D, "Searching local user for ID %s...\n", vboxuser->incomingid);
|
|
|
|
vboxuser->uid = 0;
|
|
vboxuser->gid = 0;
|
|
vboxuser->home[0] = 0
|
|
vboxuser->umask = -1;
|
|
vboxuser->space = -1;
|
|
|
|
printstring(temppathname, "%s/vboxgetty.user", VBOX_ETCDIR);
|
|
|
|
if ((rc = fopen(temppathname, "r")))
|
|
{
|
|
linenr = 0;
|
|
|
|
while (fgets(line, VBOX_RCLINE_SIZE, rc))
|
|
{
|
|
linenr++;
|
|
|
|
line[strlen(line) - 1] = '\0';
|
|
|
|
if ((stop = rindex(line, '#'))) *stop = '\0';
|
|
|
|
while (strlen(line) > 0)
|
|
{
|
|
if ((line[strlen(line) - 1] != ' ') && (line[strlen(line) - 1] != '\t')) break;
|
|
|
|
line[strlen(line) - 1] = '\0';
|
|
}
|
|
|
|
if (*line == '\0') continue;
|
|
|
|
pattern = strtok(line, ":");
|
|
name = strtok(NULL, ":");
|
|
group = strtok(NULL, ":");
|
|
mask = strtok(NULL, ":");
|
|
space = strtok(NULL, ":");
|
|
|
|
if ((!pattern) || (!name) || (!group) || (!mask) || (!space))
|
|
{
|
|
log_line(LOG_E, "Error in \"%s\" line %d.\n", temppathname, linenr);
|
|
|
|
fclose(rc);
|
|
return(-1);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
fclose(rc);
|
|
}
|
|
else
|
|
{
|
|
log_line(LOG_W, "Can't open \"%s\".\n", temppathname);
|
|
|
|
return(-1);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|