mISDNuser/capi20/daemon.c

2558 lines
67 KiB
C

/*
* mISDNv2 CAPI 2.0 daemon
*
* Written by Karsten Keil <kkeil@linux-pingi.de>
*
* Copyright (C) 2011 Karsten Keil <kkeil@linux-pingi.de>
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2 as published by the
* Free Software Foundation. See the LICENSE file included with
* this package for more details.
*/
#define _GNU_SOURCE
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <getopt.h>
#include <stdarg.h>
#include <sys/un.h>
#include <errno.h>
#include <poll.h>
#include <mISDN/q931.h>
#include <string.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <grp.h>
#include "m_capi.h"
#include "mc_buffer.h"
#include "m_capi_sock.h"
#include "alaw.h"
#ifdef USE_SOFTFAX
#include "g3_mh.h"
#endif
#include <capi_debug.h>
/* for some reasons when _XOPEN_SOURCE 600 was defined lot of other things are not longer
* defined so use this as workaround
*/
extern int posix_openpt(int flags);
extern int grantpt(int fd);
extern int unlockpt(int fd);
extern int ptsname_r(int fd, char *buf, size_t buflen);
#ifndef DEF_CONFIG_FILE
#define DEF_CONFIG_FILE "/etc/capi20.conf"
#endif
#define MISDNCAPID_VERSION "1.0"
typedef enum {
PIT_None = 0,
PIT_Control,
PIT_mISDNmain,
PIT_mISDNtimer,
PIT_CAPImain,
PIT_NewConn,
PIT_Application,
PIT_ReleasedApp,
PIT_Bchannel,
} pollInfo_t;
struct pollInfo {
pollInfo_t type;
void *data;
};
/* give 5 sec periode to allow releasing all applications */
#define MI_SHUTDOWN_DELAY 5000
static struct pollfd *mainpoll = NULL;
static struct pollInfo *pollinfo = NULL;
static int mainpoll_max = 0;
static int mainpoll_size = 0;
#define MAINPOLL_LIMIT 256
static char def_config[] = DEF_CONFIG_FILE;
static char *config_file = NULL;
static int do_daemon = 1;
static int mIsock = -1;
static int mCsock = -1;
static int mIControl[2] = {-1, -1};
static struct pController *mI_Controller = NULL;
int mI_ControllerCount = 0;
static FILE *DebugFile = NULL;
static char *DebugFileName = NULL;
static unsigned int debugmask = 0;
static int DebugUniquePLCI = 0;
int KeepTemporaryFiles = 0;
int WriteWaveFiles = 0;
static char _TempDirectory[80];
char *TempDirectory = NULL;
static struct timer_base _mICAPItimer_base;
struct timer_base *mICAPItimer_base;
#define MISDND_TEMP_DIR "/tmp"
static char *__pinfonames[] = {
"None",
"Control",
"mISDNmain",
"mISDNtimer",
"CAPImain",
"NewConn",
"Application",
"ReleasedApp",
"Bchannel",
"unknown"
};
static char *pinfo2str(pollInfo_t pit)
{
unsigned int i = pit;
if (pit > PIT_Bchannel)
i = PIT_Bchannel + 1;
return __pinfonames[i];
}
pid_t gettid(void)
{
pid_t tid;
tid = syscall(SYS_gettid);
return tid;
}
static void usage(void)
{
fprintf(stderr, "Usage: mISDNcapid [OPTIONS]\n");
fprintf(stderr, " Options are\n");
fprintf(stderr, " -?, --help this help\n");
fprintf(stderr, " -c, --config <file> use this config file - default %s\n", def_config);
fprintf(stderr, " -d, --debug <debugmask> enable additional debug (see m_capi.h for the bits)\n");
fprintf(stderr, " -D, --debug-file <debug file> use debug file (default stdout/stderr)\n");
fprintf(stderr, " -f, --foreground run in forground, not as daemon\n");
fprintf(stderr, " -k, --keeptemp do not delete temporary files (e.g. TIFF for fax)\n");
fprintf(stderr, " -u, --uniquePLCI use unique PLCI numbers - easier to follow in the debug log\n");
// fprintf(stderr, " -w, --writewavefiles write wave files for debug fax\n");
fprintf(stderr, "\n");
fprintf(stderr, "mISDNcapid Version %s\n", MISDNCAPID_VERSION);
fprintf(stderr, "\n");
}
static int opt_parse(int ac, char *av[])
{
int c;
for (;;) {
int option_index = 0;
static struct option long_options[] = {
{"help", 0, 0, '?'},
{"config", 1, 0, 'c'},
{"debug-file", 1, 0, 'D'},
{"debug", 1, 0, 'd'},
{"foreground", 0, 0, 'f'},
{"keeptemp", 0, 0, 'k'},
{"uniquePLCI", 0, 0, 'u'},
{"writewavefiles", 0, 0, 'w'},
{0, 0, 0, 0}
};
c = getopt_long(ac, av, "?c:D:d:fkuw", long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 0:
fprintf(stderr, "option %s", long_options[option_index].name);
if (optarg)
fprintf(stderr, " with arg %s", optarg);
fprintf(stderr, "\n");
break;
case 'c':
if (optarg)
config_file = optarg;
else {
fprintf(stderr, "option -c but no filename\n");
return -2;
}
break;
case 'D':
if (optarg)
DebugFileName = optarg;
else {
fprintf(stderr, "option -D but no filename\n");
return -2;
}
break;
case 'd':
if (optarg) {
errno = 0;
debugmask = (unsigned int)strtoul(optarg, NULL, 0);
if (errno) {
fprintf(stderr, "cannot read debuglevel from %s - %s\n", optarg, strerror(errno));
return -3;
}
} else {
fprintf(stderr, "option -d but no value for debugmask\n");
return -3;
}
break;
case 'f':
do_daemon = 0;
break;
case 'k':
KeepTemporaryFiles = 1;
break;
case 'u':
DebugUniquePLCI = 1;
break;
case 'w':
WriteWaveFiles++;
break;
case '?':
usage();
return -1;
}
}
c = ac - optind;
if (c != 0) {
fprintf(stderr, "unknown options: %s\n", av[optind]);
return -2;
}
return 0;
}
static int read_config_file(char *name)
{
int nr_controller = 0, lnr = 0;
int contr = 0, capicontr, ena, n;
FILE *f;
char line[256], *s;
f = fopen(name, "r");
if (!f) {
fprintf(stderr, "cannot open %s - %s\n", name, strerror(errno));
return 0;
}
while ((s = fgets(line, 256, f))) {
lnr++;
switch (*s) {
case '\n':
case '#':
case ';':
case '!':
/* comment or empty lines */
continue;
break;
default:
break;
}
if (!strncasecmp("mISDN", s, 5)) {
n = sscanf(&s[5], "%d %d %d", &contr, &capicontr, &ena);
switch (n) {
case 0:
nr_controller = -1;
fprintf(stderr, "error in config file %s:%d:%s\n", name, lnr, line);
goto err;
case 1:
capicontr = contr + 1;
case 2:
ena = 1;
break;
}
if (contr > mI_ControllerCount - 1) {
fprintf(stderr, "Controller %d not detected - ignored\n", contr + 1);
continue;
}
if (contr < 0 || contr > 126) {
fprintf(stderr, "Invalid controller nr (%d) in config file %s, line %d: %s\n", contr + 1, name,
lnr, line);
nr_controller = -2;
goto err;
}
if (capicontr < 1 || capicontr > 127) {
fprintf(stderr, "Invalid capi controller nr (%d) in config file %s, line %d: %s\n", capicontr,
name, lnr, line);
nr_controller = -3;
goto err;
}
mI_Controller[contr].mNr = contr;
mI_Controller[contr].profile.ncontroller = capicontr;
mI_Controller[contr].enable = ena;
mI_Controller[contr].cobjLC.id = capicontr;
mI_Controller[contr].cobjPLCI.id = capicontr;
nr_controller++;
}
if (!strncasecmp("debugmask", s, 9)) {
debugmask |= (unsigned int)strtol(&s[9], NULL, 0);
}
/* all other is ignored */
if (feof(f))
break;
}
err:
fclose(f);
return nr_controller;
}
int send_master_control(int event, int len, void *para)
{
int ret, err, *msg, totlen = sizeof(event);
if (len > 0) {
totlen += len;
msg = malloc(totlen);
if (!msg) {
eprint("Cannot alloc %d bytes for maincontrol message\n", totlen);
return -ENOMEM;
}
*msg = event | len;
msg++;
memcpy(msg, para, len);
msg--;
} else {
msg = &event;
}
ret = write(mIControl[1], msg, totlen);
if (ret != totlen) {
if (ret < 0)
err = errno;
else
err = EMSGSIZE;
eprint("Cannot send maincontrol message %08x return %d (%d) - %s\n", *msg, ret, totlen, strerror(err));
ret = -err;
} else {
ret = 0;
}
if (len > 0)
free(msg);
return ret;
}
/**********************************************************************
Signal handler for clean shutdown
***********************************************************************/
static void
termHandler(int sig)
{
send_master_control(MICD_CTRL_SHUTDOWN, 0, NULL);
return;
}
/**********************************************************************
Signal handler for re-opening log file
***********************************************************************/
static void
hupHandler(int sig)
{
send_master_control(MICD_CTRL_REOPEN_LOG, 0, NULL);
return;
}
/**********************************************************************
Signal handler for dumping state info
***********************************************************************/
static void dump_mainpoll(void)
{
int i;
for (i = 0; i < mainpoll_max; i++) {
iprint("Poll[%i] fd:%d mask:%04x/%04x data:%p type:%s\n",
i, mainpoll[i].fd, mainpoll[i].events, mainpoll[i].revents, pollinfo[i].data, pinfo2str(pollinfo[i].type));
}
}
static void dump_controller(void)
{
int i, j;
struct BInstance *bi;
for (i = 0; i < mI_ControllerCount; i++) {
if (mI_Controller[i].enable) {
iprint("Controller%d mISDN No:%d Bchannels:%d capiNr:%d ApplCnt:%d\n", i, mI_Controller[i].mNr,
mI_Controller[i].devinfo.nrbchan, mI_Controller[i].profile.ncontroller, mI_Controller[i].appCnt);
iprint("Controller%d InfoMask:%08x CIPMask:%08x CIP2Mask:%08x\n", i, mI_Controller[i].InfoMask,
mI_Controller[i].CIPmask, mI_Controller[i].CIPmask2);
for (j = 0; j < mI_Controller[i].BImax; j++) {
bi = &mI_Controller[i].BInstances[j];
iprint("Controller%d Bi[%d] B%d type:%s usecnt:%d proto:%x fd:%d tty:%d%s%s%s%s%s\n", i, j, bi->nr,
BItype2str(bi->type), bi->usecnt, bi->proto, bi->fd, bi->tty,
bi->closed ? " closed" : "", bi->closing ? " closing" : "", bi->running ? " running" : "",
bi->detached ? " detached" : "", bi->joined ? " joined" : "");
}
dump_lControllers(&mI_Controller[i]);
dump_controller_plci(&mI_Controller[i]);
} else {
iprint("Controller%d disabled\n", i);
}
}
}
const char *_BTypes[] = {
"None",
"Direct",
"Fax",
"tty",
"Wrong value"
};
const char *BItype2str(enum BType bt)
{
const char *r;
unsigned int i = (unsigned int)bt;
if (i <= BType_tty)
r = _BTypes[i];
else
r = _BTypes[4];
return r;
}
static void
dumpHandler(int sig)
{
if (sig == SIGUSR1)
send_master_control(MICD_CTRL_DUMP_1, 0, NULL); // master control will call do_dump
else
send_master_control(MICD_CTRL_DUMP_2, 0, NULL); // master control will call do_dump
return;
}
static void do_dump(const int sig)
{
if (sig == SIGUSR1) {
iprint("Received signal %d -- start dumping\n", sig);
dump_applications();
dump_controller();
dump_mainpoll();
mc_buffer_dump_status();
dump_cobjects();
} else {
dump_cobjects();
#ifdef MISDN_CAPIOBJ_NO_FREE
dump_cobjects_free();
#endif
}
iprint("dumping ends\n");
}
static int add_mainpoll(int fd, pollInfo_t pit)
{
struct pollfd *newmp;
struct pollInfo *newinfo;
int i, n;
if (mainpoll_max) {
for (i = 0; i < mainpoll_max; i++) {
if (mainpoll[i].fd == -1)
break;
}
} else
i = 0;
if (i == mainpoll_max) {
if (mainpoll_max < mainpoll_size)
mainpoll_max++;
else if (mainpoll_size < MAINPOLL_LIMIT) {
if (mainpoll_size)
n = mainpoll_size * 2;
else
n = 8;
newmp = calloc(n, sizeof(struct pollfd));
if (!newmp) {
eprint("no memory for %d mainpoll\n", n);
return -1;
}
newinfo = calloc(n, sizeof(struct pollInfo));
if (!newinfo) {
free(newmp);
eprint("no memory for %d pollinfo\n", n);
return -1;
}
if (mainpoll) {
memcpy(newmp, mainpoll, mainpoll_size * sizeof(struct pollfd));
free(mainpoll);
}
if (pollinfo) {
memcpy(newinfo, pollinfo, mainpoll_size * sizeof(struct pollInfo));
free(pollinfo);
}
mainpoll_size = n;
mainpoll = newmp;
pollinfo = newinfo;
mainpoll_max++;
} else {
eprint("mainpoll full %d fds\n", mainpoll_size);
return -1;
}
}
mainpoll[i].fd = fd;
pollinfo[i].type = pit;
mainpoll[i].events = POLLIN | POLLPRI;
return i;
}
static int del_mainpoll(int fd)
{
int i, j;
for (i = 0; i < mainpoll_max; i++) {
if (mainpoll[i].fd == fd) {
mainpoll[i].events = 0;
mainpoll[i].revents = 0;
mainpoll[i].fd = -1;
j = i;
switch (pollinfo[i].type) {
default:
if (pollinfo[i].data)
free(pollinfo[i].data);
case PIT_ReleasedApp: /* not freed here */
case PIT_Application: /* not freed here */
case PIT_Bchannel: /* Never freed */
case PIT_mISDNtimer:
pollinfo[i].data = NULL;
pollinfo[i].type = PIT_None;
break;
}
while (mainpoll_max && j == (mainpoll_max - 1) && mainpoll[j].fd == -1) {
mainpoll_max--;
j--;
}
return i;
}
}
return -1;
}
static int modify_mainpoll(int enable, int fd)
{
int i;
for (i = 0; i < mainpoll_max; i++) {
if (mainpoll[i].fd == fd) {
if (enable)
mainpoll[i].events = POLLIN | POLLPRI;
else
mainpoll[i].events = POLLPRI;
dprint(MIDEBUG_CONTROLLER, "Mainpoll: fd=%d now %s\n", fd, enable ? "enabled" : "disabled");
return 0;
}
}
return -EINVAL;
}
int mIcapi_mainpoll_releaseApp(int fd, int newfd)
{
int i;
for (i = 0; i < mainpoll_max; i++) {
if (mainpoll[i].fd == fd) {
if (pollinfo[i].type == PIT_Application || pollinfo[i].type == PIT_ReleasedApp) {
mainpoll[i].fd = newfd;
pollinfo[i].type = PIT_ReleasedApp;
return i;
} else {
eprint("Mainpoll fd=%d found but type %s\n", fd, BItype2str(pollinfo[i].type));
}
}
}
return -1;
}
static int main_control(int idx)
{
int ret, event, len, *fds;
void *para = NULL;
len = sizeof(event);
ret = read(mainpoll[idx].fd, &event, len);
if (ret != len) {
if (ret > 0)
event = -EMSGSIZE;
else
event = -errno;
eprint("Event for MasterControl read error read return %d (%d) - %s\n", ret, len, strerror(-event));
return event;
}
len = event & MICD_EV_LEN;
if (len) {
para = malloc(len);
if (!para) {
eprint("Event for MasterControl cannot alloc %d bytes for parameter\n", len);
return -ENOMEM;
}
ret = read(mainpoll[idx].fd, para, len);
if (ret != len) {
if (ret > 0)
event = -EMSGSIZE;
else
event = -errno;
eprint("Event for MasterControl read error read on parameter return %d (%d) - %s\n", ret, len, strerror(-event));
free(para);
return event;
}
}
switch (event & MICD_EV_MASK) {
case MICD_CTRL_SHUTDOWN:
iprint("Terminating on signal -- request shutdown mISDNcapid\n");
break;
case MICD_CTRL_REOPEN_LOG:
iprint("Re-opening log on signal\n");
if (DebugFileName)
{
iprint("Received SIGHUP, closing this file for immediate re-open\n");
fflush(DebugFile);
fclose(DebugFile);
DebugFile = fopen(DebugFileName, "a");
if (!DebugFile)
{
fprintf(stderr, "Cannot re-open %s - %s\n", DebugFileName, strerror(errno));
break;
}
iprint("Re-opened debug log file after SIGHUP\n");
fflush(DebugFile);
}
else
iprint("Nothing to do for SIGHUP since no debug file configured\n");
break;
case MICD_CTRL_DUMP_1:
do_dump(SIGUSR1);
break;
case MICD_CTRL_DUMP_2:
do_dump(SIGUSR2);
break;
case MICD_CTRL_DISABLE_POLL:
case MICD_CTRL_ENABLE_POLL:
len /= sizeof(int);
fds = para;
while (len) {
ret = modify_mainpoll((event & MICD_EV_MASK) == MICD_CTRL_ENABLE_POLL, *fds);
if (ret)
wprint("modify_mainpoll for fd=%d failed\n", *fds);
len--;
fds++;
}
break;
default:
eprint("Unknown event for MasterControl %08x - ignored\n", event);
event = -EINVAL;
}
if (para)
free(para);
return event;
}
void clean_all(void)
{
int i, j;
struct mApplication *ap;
struct mCAPIobj *co;
struct lController *lc;
dprint(MIDEBUG_CONTROLLER, "clean_all controller:%d mainpoll %d/%d\n", mI_ControllerCount, mainpoll_max, mainpoll_size);
if (!mainpoll_max && !mainpoll_size) {
wprint("clean_all called twice\n");
return;
}
for (i = 0; i < mainpoll_max; i++) {
switch (pollinfo[i].type) {
case PIT_Control:
dprint(MIDEBUG_CONTROLLER, "close mIControl(%d, %d)\n", mIControl[0], mIControl[1]);
if (mIControl[0] >= 0) {
close(mIControl[0]);
mIControl[0] = -1;
}
if (mIControl[1] >= 0) {
close(mIControl[1]);
mIControl[1] = -1;
}
break;
case PIT_Application:
ap = pollinfo[i].data;
if (ap) {
eprint("Application %d not released in clean_all freeing now\n", ap->cobj.id2);
pollinfo[i].data = NULL;
ReleaseApplication(ap, 0);
Free_Application(&ap->cobj);
} else
eprint("Unlinked Application not released in clean_all\n");
break;
case PIT_Bchannel:
ReleaseBchannel(pollinfo[i].data);
break;
case PIT_NewConn:
dprint(MIDEBUG_CONTROLLER, "close mainpoll[%d].fd %d\n", i, mainpoll[i].fd);
close(mainpoll[i].fd);
break;
case PIT_ReleasedApp:
ap = pollinfo[i].data;
if (ap) {
eprint("Released Application %d in clean_all freeing now\n", ap->cobj.id2);
pollinfo[i].data = NULL;
Free_Application(&ap->cobj);
}
break;
case PIT_mISDNmain:
case PIT_mISDNtimer:
case PIT_CAPImain:
case PIT_None:
break;
}
mainpoll[i].fd = -1;
mainpoll[i].events = 0;
mainpoll[i].revents = 0;
}
mainpoll_max = 0;
mainpoll_size = 0;
if (pollinfo)
free(pollinfo);
pollinfo = NULL;
if (mainpoll)
free(mainpoll);
mainpoll = NULL;
for (i = 0; i < mI_ControllerCount; i++) {
dprint(MIDEBUG_CONTROLLER, "clean_all controller:%d BImax=%d\n", i, mI_Controller[i].BImax);
if (mI_Controller[i].l3) {
close_layer3(mI_Controller[i].l3);
mI_Controller[i].l3 = NULL;
}
if (mI_Controller[i].BInstances) {
for (j = 0; j < mI_Controller[i].BImax; j++) {
ReleaseBchannel(&mI_Controller[i].BInstances[j]);
}
free(mI_Controller[i].BInstances);
mI_Controller[i].BInstances = NULL;
}
co = get_next_cobj(&mI_Controller[i].cobjLC, NULL);
while (co) {
lc = container_of(co, struct lController, cobj);
Free_lController(&lc->cobj);
co = get_next_cobj(&mI_Controller[i].cobjLC, co);
}
}
mI_ControllerCount = 0;
}
struct pController *get_cController(int cNr)
{
int i;
for (i = 0; i < mI_ControllerCount; i++) {
if (mI_Controller[i].enable && mI_Controller[i].profile.ncontroller == cNr)
return &mI_Controller[i];
}
return NULL;
}
struct pController *get_mController(int mNr)
{
int i;
for (i = 0; i < mI_ControllerCount; i++) {
if (mI_Controller[i].enable && mI_Controller[i].mNr == mNr)
return &mI_Controller[i];
}
return NULL;
}
/* Is called with pc->cobjPLCI.lock !!! */
uint32_t NextFreePLCI(struct mCAPIobj *copc)
{
uint32_t id = 0, last;
uint8_t run;
struct pController *pc;
struct mCAPIobj *c;
pc = container_of(copc, struct pController, cobjPLCI);
if (DebugUniquePLCI) {
run = 2; /* one wrap after reaching 0xff00 allowed */
pc->lastPLCI += 0x0100;
if (pc->lastPLCI > 0xff00) {
pc->lastPLCI = 0x0100;
run--;
}
last = pc->lastPLCI;
} else {
last = 0x0100;
run = 1; /* stop after reaching 0xff00 */
}
/* check if not used */
while (run) {
c = copc->listhead;
while (c) {
if ((c->id & 0xff00) == last)
break;
c = c->next;
}
if (!c) {
id = last;
break;
} else {
if (last == 0xff00) {
run--;
last = 0x0100;
} else {
last += 0x0100;
}
}
}
if (id)
pc->lastPLCI = id;
return id;
}
struct BInstance *ControllerSelChannel(struct pController *pc, int nr, int proto)
{
struct BInstance *bi;
int pmask, err;
if (nr >= pc->BImax) {
wprint("Request for channel number %d but controller %d only has %d channels\n",
nr, pc->profile.ncontroller, pc->devinfo.nrbchan);
return NULL;
}
if (ISDN_P_B_START <= proto) {
pmask = 1 << (proto & ISDN_P_B_MASK);
if (!(pmask & pc->devinfo.Bprotocols)) {
wprint("Request for channel number %d on controller %d protocol 0x%02x not supported\n",
nr, pc->profile.ncontroller, proto);
return NULL;
}
} else {
pmask = 1 << proto;
if (!(pmask & pc->devinfo.Dprotocols)) {
wprint("Request for channel number %d on controller %d protocol 0x%02x not supported\n",
nr, pc->profile.ncontroller, proto);
return NULL;
}
}
bi = pc->BInstances + nr;
err = pthread_mutex_lock(&bi->lock);
if (err == 0) {
if (bi->usecnt) {
/* for now only one user allowed - this is not sufficient for X25 */
wprint("Request for channel number %d on controller %d but channel already in use\n",
nr, pc->profile.ncontroller);
pthread_mutex_unlock(&bi->lock);
bi = NULL;
} else {
bi->usecnt++;
bi->proto = proto;
pthread_mutex_unlock(&bi->lock);
}
} else {
eprint("Controller%d lock for BI[%d] could not aquired - %s\n",
pc->profile.ncontroller, bi->nr, strerror(err));
bi = NULL;
}
return bi;
}
int ControllerDeSelChannel(struct BInstance *bi)
{
int err;
err = pthread_mutex_lock(&bi->lock);
if (err == 0) {
if (bi->usecnt) {
bi->usecnt--;
bi->proto = ISDN_P_NONE;
} else {
wprint("BI[%d] usage count is already zero\n",
bi->nr);
err = -EINVAL;
}
pthread_mutex_unlock(&bi->lock);
} else {
eprint("Lock for BI[%d] could not be acquired - %s\n",
bi->nr, strerror(err));
}
return err;
}
int check_free_bchannels(struct pController *pc)
{
int i, cnt = 0;
if (pc) {
for (i = 0; i <= pc->BImax; i++) {
if (test_channelmap(i, pc->devinfo.channelmap)) {
if (pc->BInstances[i].usecnt == 0)
cnt++;
}
}
}
return cnt;
}
static int Create_tty(struct BInstance *bi)
{
int ret, pmod;
ret = posix_openpt(O_RDWR | O_NOCTTY);
if (ret < 0) {
eprint("Cannot open terminal - %s\n", strerror(errno));
} else {
bi->tty = ret;
dprint(MIDEBUG_CONTROLLER, "create bi[%d]->tty %d\n", bi->nr, bi->tty);
ret = grantpt(bi->tty);
if (ret < 0) {
eprint("Error on grantpt - %s\n", strerror(errno));
dprint(MIDEBUG_CONTROLLER, "close bi[%d]->tty %d\n", bi->nr, bi->tty);
close(bi->tty);
bi->tty = -1;
} else {
ret = unlockpt(bi->tty);
if (ret < 0) {
eprint("Error on unlockpt - %s\n", strerror(errno));
dprint(MIDEBUG_CONTROLLER, "close bi[%d]->tty %d\n", bi->nr, bi->tty);
close(bi->tty);
bi->tty = -1;
} else {
/* packet mode */
pmod = 1;
ret = ioctl(bi->tty, TIOCPKT, &pmod);
if (ret < 0) {
eprint("Cannot set packet mode - %s\n", strerror(errno));
dprint(MIDEBUG_CONTROLLER, "close bi[%d]->tty %d\n", bi->nr, bi->tty);
close(bi->tty);
bi->tty = -1;
} else {
bi->tty_received = 0;
}
}
}
}
return ret;
}
static int recvBchannel(struct BInstance *);
static int recv_tty(struct BInstance *bi)
{
int ret, maxl;
struct mc_buf *mc;
mc = alloc_mc_buf();
if (!mc)
return -ENOMEM;
if (!bi)
return -EINVAL;
mc->rp = mc->rb + 8;
maxl = MC_RB_SIZE - 8;
ret = read(bi->tty, mc->rp, maxl);
if (ret < 0) {
wprint("Error on reading from tty %d errno %d - %s\n", bi->tty, errno, strerror(errno));
ret = -errno;
} else if (ret == 0) {
/* closed */
wprint("Read 0 bytes from tty %d\n", bi->tty);
ret = -ECONNABORTED;
} else if (ret == maxl) {
eprint("Message too big %d ctrl %02x (%02x%02x%02x%02x%02x%02x%02x%02x)\n",
ret, mc->rp[0], mc->rp[1], mc->rp[2], mc->rp[3], mc->rp[4], mc->rp[5], mc->rp[6], mc->rp[7], mc->rp[8]);
ret = -EMSGSIZE;
}
if (ret > 0) {
bi->tty_received = 1;
mc->len = ret;
/* Fake length of DATA_B3 REQ to pass offset check */
capimsg_setu16(mc->rb, 0, 22);
mc->cmsg.Command = CAPI_DATA_TTY;
mc->cmsg.Subcommand = CAPI_REQ;
ret = bi->from_up(bi, mc);
}
if (ret != 0) /* if message is not queued or freed */
free_mc_buf(mc);
return ret;
}
static char *_threadmsg1 = "running";
static char *_threadmsg2 = "stopped after poll";
static char *_threadmsg3 = "stopped after receive";
static char *_threadmsg4 = "stopped via command";
static void *BCthread(void *arg)
{
struct BInstance *bi = arg;
unsigned char cmd;
char *msg = _threadmsg1;
int ret, i;
bi->running = 1;
bi->got_timeout = 0;
bi->detached = 0;
bi->joined = 0;
bi->release_pending = 0;
bi->tid = gettid();
iprint("Started Bchannel thread=%05d\n", bi->tid);
while (bi->running) {
if (bi->pcnt == 0) {
wprint("Bchannel%d no filedescriptors to poll - abort thread=%05d\n",
bi->nr, bi->tid);
break;
}
ret = poll(bi->pfd, bi->pcnt, bi->timeout);
if (!bi->running) {
msg = _threadmsg2;
break;
}
if (ret < 0) {
wprint("Bchannel%d Error on poll - %s\n", bi->nr, strerror(errno));
continue;
}
if (ret == 0) { /* timeout */
if (bi->release_pending) {
wprint("Bchannel%d %stimeout (release pending) thread=%05d\n", bi->nr,
bi->got_timeout ? "2. " : "", bi->tid);
if (bi->got_timeout) { /* 2 times */
bi->detached = 1;
ret = pthread_detach(bi->thread);
if (ret)
wprint("Error on pthread_detach thread=%05d - %s\n", bi->tid, strerror(ret));
bi->running = 0;
} else
bi->got_timeout = 1;
ret = bi->from_down(bi, NULL);
if (ret < 0)
wprint("Bchannel%d from down RELEASE return %d - %s\n", bi->nr, ret, strerror(-ret));
}
continue;
}
for (i = 1; i < bi->pcnt; i++) {
if (bi->pfd[i].revents & POLLIN) {
switch(i) {
case 1:
ret = recvBchannel(bi);
break;
case 2:
ret = recv_tty(bi);
break;
default:
wprint("Bchannel%d poll idx %d not handled\n", bi->nr, i);
}
}
}
if (!bi->running) {
msg = _threadmsg3;
break;
}
if (bi->pfd[0].revents & POLLIN) {
ret = read(bi->pfd[0].fd, &cmd, 1);
iprint("Got control %d thread=%05d\n", cmd, bi->tid);
if (cmd == 42) {
bi->running = 0;
msg = _threadmsg4;
}
}
bi->got_timeout = 0;
}
iprint("thread=%05d terminating with msg %s\n", bi->tid, msg);
return msg;
}
static int CreateBchannelThread(struct BInstance *bi, int pcnt)
{
int ret, i;
if (bi->cpipe[0] > -1 || bi->cpipe[1] > -1)
wprint("bi[%d]->cpipe(%d, %d) still open - may leaking fds\n", bi->nr, bi->cpipe[0], bi->cpipe[1]);
ret = pipe(bi->cpipe);
if (ret) {
eprint("error - %s\n", strerror(errno));
return ret;
} else
dprint(MIDEBUG_CONTROLLER, "create bi[%d]->cpipe(%d, %d)\n", bi->nr, bi->cpipe[0], bi->cpipe[1]);
ret = fcntl(bi->cpipe[0], F_SETFL, O_NONBLOCK);
if (ret) {
eprint("error - %s\n", strerror(errno));
return ret;
}
ret = fcntl(bi->cpipe[1], F_SETFL, O_NONBLOCK);
if (ret) {
eprint("error - %s\n", strerror(errno));
return ret;
}
bi->pfd[0].fd = bi->cpipe[0];
bi->pfd[0].events = POLLIN | POLLPRI;
bi->pfd[1].fd = bi->fd;
bi->pfd[1].events = POLLIN | POLLPRI;
bi->pcnt = pcnt;
bi->timeout = 500; /* default poll timeout 500ms */
ret = pthread_create(&bi->thread, NULL, BCthread, bi);
if (ret) {
eprint("Cannot create thread error - %s\n", strerror(errno));
bi->detached = 1;
dprint(MIDEBUG_CONTROLLER, "close bi[%d]->cpipe(%d, %d)\n", bi->nr, bi->cpipe[0], bi->cpipe[1]);
close(bi->cpipe[0]);
close(bi->cpipe[1]);
bi->cpipe[0] = -1;
bi->cpipe[1] = -1;
bi->pfd[0].fd = -1;
for (i = 1; i < bi->pcnt; i++)
bi->pfd[i].fd = -1;
}
return ret;
}
static int StopBchannelThread(struct BInstance *bi)
{
int ret, i, result = 0;
unsigned char cmd;
char *msg = NULL;
pthread_t self = pthread_self();
if (bi->running) {
if (bi->waiting)
sem_post(&bi->wait);
if (pthread_equal(self, bi->thread)) {
/* we cannot join our self but we will terminate on return */
bi->detached = 1;
ret = pthread_detach(bi->thread);
if (ret) {
wprint("Error on pthread_detach thread=%05d - %s\n", bi->tid, strerror(ret));
result = -ret;
}
bi->running = 0;
dprint(MIDEBUG_CONTROLLER, "thread=%05d detached\n", bi->tid);
} else if (!bi->joined) {
cmd = 42;
ret = write(bi->cpipe[1], &cmd, 1);
if (ret != 1)
wprint("Error on write control ret=%d - %s\n", ret, strerror(errno));
bi->joined = 1;
ret = pthread_join(bi->thread, (void **)&msg);
if (ret) {
wprint("Error on pthread_join - %s\n", strerror(ret));
result = -ret;
} else
dprint(MIDEBUG_CONTROLLER, "thread=%05d joined to %05d %s\n", bi->tid, gettid(), msg);
} else {
wprint("Running but already joined in thread=%05d ???\n", bi->tid);
}
dprint(MIDEBUG_CONTROLLER, "close bi[%d]->cpipe(%d, %d)\n", bi->nr, bi->cpipe[0], bi->cpipe[1]);
close(bi->cpipe[0]);
close(bi->cpipe[1]);
bi->pfd[0].fd = -1;
for (i = 1; i < bi->pcnt; i++)
bi->pfd[i].fd = -1;
bi->pcnt = 0;
bi->cpipe[0] = -1;
bi->cpipe[1] = -1;
} else {
if (!bi->detached) {
if (!bi->joined) {
if (pthread_equal(self, bi->thread)) {
/* we cannot join our self but we will terminate on return */
wprint("Not running but still in thread=%05d - possible memleak\n", bi->tid);
result = -EDEADLOCK;
} else {
bi->joined = 1;
ret = pthread_join(bi->thread, (void **)&msg);
if (ret) {
wprint("Error on pthread_join thread=%05d - %s\n", bi->tid, strerror(ret));
result = -ret;
} else
dprint(MIDEBUG_CONTROLLER, "thread=%05d joined to %05d %s\n", bi->tid, gettid(), msg);
}
}
}
}
return result;
}
static int dummy_btrans(struct BInstance *bi, struct mc_buf *mc)
{
struct mISDNhead *hh;
if (!mc) {
wprint("Controller%d ch%d: Got timeout RELEASE - but %s called thread=%x\n", bi->pc->profile.ncontroller, bi->nr,
__func__, (unsigned int)pthread_self());
return 0;
} else {
hh = (struct mISDNhead *)mc->rb;
wprint("Controller%d ch%d: Got %s id %x - but %s called thread=%x\n", bi->pc->profile.ncontroller, bi->nr,
_mi_msg_type2str(hh->prim), hh->id, __func__, (unsigned int)pthread_self());
return -EINVAL;
}
}
int OpenBInstance(struct BInstance *bi, struct lPLCI *lp)
{
int sk;
int ret;
struct sockaddr_mISDN addr;
struct mISDN_ctrl_req creq;
sk = socket(PF_ISDN, SOCK_DGRAM, bi->proto);
if (sk < 0) {
wprint("Cannot open socket for BInstance %d on controller %d protocol 0x%02x - %s\n",
bi->nr, bi->pc->profile.ncontroller, bi->proto, strerror(errno));
return -errno;
} else
dprint(MIDEBUG_CONTROLLER, "create socket bi[%d]->fd %d\n", bi->nr, sk);
ret = fcntl(sk, F_SETFL, O_NONBLOCK);
if (ret < 0) {
ret = -errno;
wprint("fcntl error %s\n", strerror(errno));
dprint(MIDEBUG_CONTROLLER, "close socket bi[%d]->fd %d\n", bi->nr, sk);
close(sk);
return ret;
}
if (get_cobj(&lp->cobj)) {
bi->lp = lp;
} else {
bi->lp = NULL;
ret = -EINVAL;
wprint("Cannot get logical controller object\n");
close(sk);
return ret;
}
switch (lp->btype) {
case BType_Direct:
bi->from_down = recvBdirect;
bi->from_up = ncciB3Data;
break;
#ifdef USE_SOFTFAX
case BType_Fax:
bi->from_down = FaxRecvBData;
bi->from_up = FaxB3Message;
break;
#endif
case BType_tty:
bi->from_down = recvBdirect;
bi->from_up = ncciB3Data;
break;
default:
eprint("Error unnkown BType %d\n", lp->btype);
dprint(MIDEBUG_CONTROLLER, "close socket bi[%d]->fd %d\n", bi->nr, sk);
close(sk);
bi->lp = NULL;
put_cobj(&lp->cobj);
return -EINVAL;
}
bi->type = lp->btype;
addr.family = AF_ISDN;
addr.dev = bi->pc->mNr;
addr.channel = bi->nr;
/* not used but make valgrind happy */
addr.sapi = 0;
addr.tei = 0;
ret = bind(sk, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0) {
ret = -errno;
wprint("Cannot bind socket for BInstance %d on controller %d (mISDN nr %d) protocol 0x%02x - %s\n",
bi->nr, bi->pc->profile.ncontroller, bi->pc->mNr, bi->proto, strerror(errno));
dprint(MIDEBUG_CONTROLLER, "close socket bi[%d]->fd %d\n", bi->nr, sk);
close(sk);
bi->from_down = dummy_btrans;
bi->from_up = dummy_btrans;
bi->type = BType_None;
bi->lp = NULL;
put_cobj(&lp->cobj);
} else {
bi->closed = 0;
bi->fd = sk;
bi->org_rx_min = MISDN_CTRL_RX_SIZE_IGNORE;
bi->rx_min = MISDN_CTRL_RX_SIZE_IGNORE;
bi->org_rx_max = MISDN_CTRL_RX_SIZE_IGNORE;
bi->rx_max = MISDN_CTRL_RX_SIZE_IGNORE;
if ((bi->proto == ISDN_P_B_L2DSP) || (bi->proto == ISDN_P_B_RAW)) {
/* Get buffersize */
creq.op = MISDN_CTRL_RX_BUFFER;
creq.channel = bi->nr;
creq.p1 = MISDN_CTRL_RX_SIZE_IGNORE; // do not change min yet
creq.p2 = MISDN_CTRL_RX_SIZE_IGNORE; // do not change max yet
creq.unused = 0;
ret = ioctl(bi->fd, IMCTRLREQ, &creq);
/* MISDN_CTRL_RX_BUFFER is not mandatory warn if not supported */
if (ret < 0) {
wprint("%s: Error on MISDN_CTRL_RX_BUFFER ioctl maybe kernel do not support it - %s\n",
CAPIobjIDstr(&lp->cobj), strerror(errno));
} else {
bi->org_rx_min = creq.p1;
bi->org_rx_max = creq.p2;
dprint(MIDEBUG_NCCI, "%s: MISDN_CTRL_RX_BUFFER original values: min=%d max=%d\n",
CAPIobjIDstr(&lp->cobj), creq.p1, creq.p2);
if (bi->org_rx_max > lp->Appl->MaxB3Size)
bi->rx_max = lp->Appl->MaxB3Size;
else
bi->rx_max = bi->org_rx_max;
if (bi->type == BType_Fax) {
bi->rx_min = DEFAULT_FAX_PKT_SIZE;
} else {
bi->rx_min = bi->rx_max - (bi->org_rx_min - 1);
if (bi->rx_min < bi->org_rx_min)
bi->rx_min = bi->org_rx_min;
}
if (bi->rx_min > bi->rx_max)
bi->rx_min = bi->rx_max;
/* Set buffersize */
creq.op = MISDN_CTRL_RX_BUFFER;
creq.channel = bi->nr;
creq.p1 = bi->rx_min;
creq.p2 = bi->rx_max;
creq.unused = 0;
ret = ioctl(bi->fd, IMCTRLREQ, &creq);
if (ret < 0) {
wprint("%s: Error setting MISDN_CTRL_RX_BUFFER min=%d max=%d ioctl - %s\n",
CAPIobjIDstr(&lp->cobj), bi->rx_min, bi->rx_max, strerror(errno));
} else {
dprint(MIDEBUG_NCCI, "%s: set rxbuffer to min=%d max=%d\n",
CAPIobjIDstr(&lp->cobj), bi->rx_min, bi->rx_max);
}
}
}
if (bi->type == BType_Direct) {
ret = add_mainpoll(sk, PIT_Bchannel);
if (ret < 0) {
eprint("Error while adding mIsock to mainpoll (mainpoll_max %d)\n", mainpoll_max);
dprint(MIDEBUG_CONTROLLER, "close socket bi[%d]->fd %d\n", bi->nr, sk);
close(sk);
bi->fd = -1;
bi->lp = NULL;
put_cobj(&lp->cobj);
} else {
dprint(MIDEBUG_CONTROLLER, "Controller%d: Bchannel %d socket %d added to poll idx %d\n",
bi->pc->profile.ncontroller, bi->nr, sk, ret);
pollinfo[ret].data = bi;
ret = 0;
bi->UpId = 0;
bi->DownId = 0;
}
} else if (bi->type == BType_Fax) {
ret = CreateBchannelThread(bi, 2);
if (ret < 0) {
eprint("Error while creating B%d-channel thread\n", bi->nr);
dprint(MIDEBUG_CONTROLLER, "close socket bi[%d]->fd %d\n", bi->nr, sk);
close(sk);
bi->fd = -1;
bi->lp = NULL;
put_cobj(&lp->cobj);
} else {
ret = 0;
bi->UpId = 0;
bi->DownId = 0;
}
} else if (bi->type == BType_tty) {
ret = Create_tty(bi);
if (ret < 0) {
eprint("Error while creating B%d-channel tty\n", bi->nr);
dprint(MIDEBUG_CONTROLLER, "close socket bi[%d]->fd %d\n", bi->nr, sk);
close(sk);
bi->fd = -1;
bi->lp = NULL;
put_cobj(&lp->cobj);
return ret;
} else {
bi->pfd[2].fd = bi->tty;
bi->pfd[2].events = POLLIN | POLLPRI;
ret = CreateBchannelThread(bi, 3);
}
if (ret < 0) {
eprint("Error while creating B%d-channel thread\n", bi->nr);
dprint(MIDEBUG_CONTROLLER, "close socket bi[%d]->fd %d\n", bi->nr, sk);
close(sk);
bi->fd = -1;
bi->lp = NULL;
put_cobj(&lp->cobj);
} else {
ret = 0;
bi->UpId = 0;
bi->DownId = 0;
}
}
if (ret)
bi->type = BType_None;
}
return ret;
}
int CloseBInstance(struct BInstance *bi)
{
int err, ret = 0;
dprint(MIDEBUG_CONTROLLER, "Closing bchannel type:%d tid=%05d usecnt:%d%s%s%s%s%s\n",
bi->type, bi->tid, bi->usecnt, bi->closed ? " closed" : "",
bi->closing ? " closing" : "", bi->running ? " running" : "",
bi->detached ? " detached" : "", bi->joined ? " joined" : "");
err = pthread_mutex_lock(&bi->lock);
if (err == 0) {
if (!bi->closed) {
if (!bi->closing) {
bi->closing = 1;
if (bi->usecnt) {
bi->closed = 1;
pthread_mutex_unlock(&bi->lock);
switch (bi->type) {
case BType_Direct:
if (bi->fd >= 0)
del_mainpoll(bi->fd);
break;
#ifdef USE_SOFTFAX
case BType_Fax:
StopBchannelThread(bi);
break;
#endif
case BType_tty:
StopBchannelThread(bi);
dprint(MIDEBUG_CONTROLLER, "close bi[%d]->tty %d\n", bi->nr, bi->tty);
if (bi->tty > -1)
close(bi->tty);
bi->tty = -1;
default:
break;
}
dprint(MIDEBUG_CONTROLLER, "Closing fd=%d usecnt %d\n", bi->fd, bi->usecnt);
if (bi->fd >= 0)
close(bi->fd);
bi->fd = -1;
bi->proto = ISDN_P_NONE;
if (bi->b3data && bi->lp)
B3ReleaseLink(bi->lp, bi);
if (bi->lp) {
put_cobj(&bi->lp->cobj);
bi->lp = NULL;
}
bi->from_up(bi, NULL);
bi->b3data = NULL;
bi->lp = NULL;
bi->type = BType_None;
bi->from_down = dummy_btrans;
bi->from_up = dummy_btrans;
err = pthread_mutex_lock(&bi->lock);
if (err == 0) {
bi->usecnt--;
bi->closing = 0;
if (bi->usecnt)
wprint("BI[%d] still in use (%d)\n", bi->nr, bi->usecnt);
pthread_mutex_unlock(&bi->lock);
ret = 0;
} else {
eprint("Lock for BI[%d] could not aquired - %s\n",
bi->nr, strerror(err));
ret = -err;
}
} else {
bi->closing = 0;
pthread_mutex_unlock(&bi->lock);
wprint("BI[%d] not active\n", bi->nr);
ret = -1;
}
} else {
pthread_mutex_unlock(&bi->lock);
wprint("Closing BI[%d] already in progress\n", bi->nr);
ret = -1;
}
} else {
pthread_mutex_unlock(&bi->lock);
wprint("Closing BI[%d] already closed\n", bi->nr);
ret = -1;
}
} else {
eprint("Lock for BI[%d] could not aquired - %s\n",
bi->nr, strerror(err));
ret = -err;
}
return ret;
};
int activate_bchannel(struct BInstance *bi)
{
int ret, err;
struct mISDNhead mh;
mh.id = 1;
if (bi->proto == ISDN_P_NONE)
return -EINVAL;
else
mh.prim = PH_ACTIVATE_REQ;
ret = send(bi->fd, &mh, sizeof(mh), 0);
if (ret != sizeof(mh)) {
err = errno;
wprint("BI[%d] cannot send activation ret %d - %s\n", bi->nr, ret, strerror(err));
if (ret == -1)
ret = -err;
else
ret = -EMSGSIZE;
} else
ret = 0;
return ret;
}
static int recvBchannel(struct BInstance *bi)
{
int ret;
struct mc_buf *mc;
mc = alloc_mc_buf();
if (!mc)
return -ENOMEM;
if (!bi) {
free_mc_buf(mc);
eprint("recvBchannel: but no BInstance assigned\n");
return -EINVAL;
}
ret = recv(bi->fd, mc->rb, MC_RB_SIZE, MSG_DONTWAIT);
if (ret < 0) {
wprint("Error on reading from %d errno %d - %s\n", bi->fd, errno, strerror(errno));
ret = -errno;
} else if (ret == 0) {
/* closed */
wprint("Nothing read - connection closed ?\n");
ret = -ECONNABORTED;
} else if (ret < 8) {
eprint("Short message read len %d (%02x%02x%02x%02x%02x%02x%02x%02x)\n",
ret, mc->rb[0], mc->rb[1], mc->rb[2], mc->rb[3], mc->rb[4], mc->rb[5], mc->rb[6], mc->rb[7]);
ret = -EBADMSG;
} else if (ret == MC_RB_SIZE) {
eprint("Message too big %d (%02x%02x%02x%02x%02x%02x%02x%02x)\n",
ret, mc->rb[0], mc->rb[1], mc->rb[2], mc->rb[3], mc->rb[4], mc->rb[5], mc->rb[6], mc->rb[7]);
ret = -EMSGSIZE;
}
if (ret > 0) {
mc->len = ret;
ret = bi->from_down(bi, mc);
}
if (ret != 0) /* if message is not queued or freed */
free_mc_buf(mc);
return ret;
}
int ReleaseBchannel(struct BInstance *bi)
{
if (!bi)
return -1;
if (bi->fd >= 0) {
del_mainpoll(bi->fd);
dprint(MIDEBUG_CONTROLLER, "close bi[%d]->fd %d\n", bi->nr, bi->fd);
close(bi->fd);
bi->fd = -1;
}
if (bi->tty >= 0) {
dprint(MIDEBUG_CONTROLLER, "close bi[%d]->tty %d\n", bi->nr, bi->tty);
close(bi->tty);
bi->tty = -1;
}
return 0;
}
static int l3_callback(struct mlayer3 *l3, unsigned int cmd, unsigned int pid, struct l3_msg *l3m)
{
struct pController *pc = l3->priv;
struct mPLCI *plci;
int ret = 0;
dprint(MIDEBUG_CONTROLLER, "Controller %d - got %s (%x) from layer3 pid(%x) msg(%p)\n",
pc->profile.ncontroller, _mi_msg_type2str(cmd), cmd, pid, l3m);
plci = getPLCI4pid(pc, pid);
switch (cmd) {
case MT_SETUP:
if (plci) {
iprint("Controller %d - got %s but pid(%x) already in use\n",
pc->profile.ncontroller, _mi_msg_type2str(cmd), pid);
break;
}
plci = new_mPLCI(pc, pid);
if (!plci) {
wprint("Controller %d - got %s but could not allocate new PLCI\n",
pc->profile.ncontroller, _mi_msg_type2str(cmd));
ret = -ENOMEM;
}
break;
case MT_SETUP_ACKNOWLEDGE:
case MT_CALL_PROCEEDING:
case MT_ALERTING:
case MT_PROGRESS:
case MT_CONNECT:
case MT_CONNECT_ACKNOWLEDGE:
case MT_DISCONNECT:
case MT_RELEASE:
case MT_RELEASE_COMPLETE:
case MT_HOLD:
case MT_HOLD_ACKNOWLEDGE:
case MT_HOLD_REJECT:
case MT_RETRIEVE:
case MT_RETRIEVE_ACKNOWLEDGE:
case MT_RETRIEVE_REJECT:
case MT_SUSPEND_ACKNOWLEDGE:
case MT_SUSPEND_REJECT:
case MT_RESUME_ACKNOWLEDGE:
case MT_RESUME_REJECT:
case MT_NOTIFY:
if (!plci)
wprint("Controller %d - got %s but no PLCI found\n", pc->profile.ncontroller, _mi_msg_type2str(cmd));
break;
case MT_FREE:
if (!plci) /* Normal on release */
dprint(MIDEBUG_CONTROLLER, "Controller %d - got %s but no PLCI found\n",
pc->profile.ncontroller, _mi_msg_type2str(cmd));
else
plci->cobj.id2 = MISDN_PID_NONE;
break;
case MPH_ACTIVATE_IND:
case MPH_DEACTIVATE_IND:
case MPH_INFORMATION_IND:
case MT_L2ESTABLISH:
case MT_L2RELEASE:
case MT_L2IDLE:
break;
case MT_TIMEOUT:
iprint("Controller %d - got %s from layer3 pid(%x) msg(%p) plci(%04x)\n",
pc->profile.ncontroller, _mi_msg_type2str(cmd), pid, l3m, plci ? plci->cobj.id : 0xffff);
break;
case MT_ERROR:
wprint("Controller %d - got %s from layer3 pid(%x) msg(%p) plci(%04x)\n",
pc->profile.ncontroller, _mi_msg_type2str(cmd), pid, l3m, plci ? plci->cobj.id : 0xffff);
break;
default:
wprint("Controller %d - got %s (%x) from layer3 pid(%x) msg(%p) plci(%04x) - not handled\n",
pc->profile.ncontroller, _mi_msg_type2str(cmd), cmd, pid, l3m, plci ? plci->cobj.id : 0xffff);
ret = -EINVAL;
}
if (!ret) {
if (plci)
ret = plci_l3l4(plci, cmd, l3m);
else if (l3m)
ret = 1; /* message need to be freed */
}
if (plci)
put_cobj(&plci->cobj);
return ret;
}
int OpenLayer3(struct pController *pc)
{
int ret = 0;
if (!pc->l3) {
pc->l3 = open_layer3(pc->mNr, pc->L3Proto, pc->L3Flags, l3_callback, pc);
if (!pc->l3) {
eprint("Cannot open L3 for controller %d L3 protocol %x L3 flags %x\n", pc->mNr, pc->L3Proto,
pc->L3Flags);
ret = -EINVAL;
} else
dprint(MIDEBUG_CONTROLLER, "Controller %d l3 open for protocol %x L3 flags %x\n", pc->mNr,
pc->L3Proto, pc->L3Flags);
}
return ret;
}
int ListenController(struct pController *pc)
{
struct mCAPIobj *co;
struct lController *lc;
uint32_t InfoMask = 0, CIPMask = 0, CIPMask2 = 0;
int ret = 0;
pthread_rwlock_rdlock(&pc->cobjLC.lock);
co = pc->cobjLC.listhead;
while (co) {
lc = container_of(co, struct lController, cobj);
dprint(MIDEBUG_CONTROLLER, "Lc %p %08x/%08x/%08x\n", lc, lc->InfoMask, lc->CIPmask, lc->CIPmask2);
InfoMask |= lc->InfoMask;
CIPMask |= lc->CIPmask;
CIPMask2 |= lc->CIPmask2;
co = co->next;
}
pthread_rwlock_unlock(&pc->cobjLC.lock);
dprint(MIDEBUG_CONTROLLER, "Controller %d change InfoMask %08x -> %08x\n", pc->profile.ncontroller, pc->InfoMask, InfoMask);
dprint(MIDEBUG_CONTROLLER, "Controller %d change CIPMask %08x -> %08x\n", pc->profile.ncontroller, pc->CIPmask, CIPMask);
dprint(MIDEBUG_CONTROLLER, "Controller %d change CIPMask2 %08x -> %08x\n", pc->profile.ncontroller, pc->CIPmask2, CIPMask2);
pc->InfoMask = InfoMask;
pc->CIPmask = CIPMask;
pc->CIPmask2 = CIPMask2;
if ((pc->CIPmask || pc->InfoMask) && !pc->l3) {
ret = OpenLayer3(pc);
}
return ret;
}
static void ShutdownAppl(int idx, int unregister)
{
struct mApplication *appl = pollinfo[idx].data;
int ret;
if (!appl) {
eprint("Application not assigned\n");
return;
}
if (appl->cpipe[0] > -1 || appl->cpipe[1] > -1)
wprint("%s appl->cpipe(%d, %d) still open - leaking fds\n", CAPIobjIDstr(&appl->cobj), appl->cpipe[0], appl->cpipe[1]);
ret = pipe2(appl->cpipe, O_NONBLOCK);
if (ret) {
eprint("Cannot open application %d control pipe - %s\n", appl->cobj.id2, strerror(errno));
mainpoll[idx].fd = -1;
} else {
dprint(MIDEBUG_CONTROLLER, "create appl->cpipe(%d, %d)\n", appl->cpipe[0], appl->cpipe[1]);
}
ReleaseApplication(appl, unregister);
pollinfo[idx].type = PIT_ReleasedApp;
mainpoll[idx].fd = appl->cpipe[0];
}
void capi_dump_shared(void);
static void get_profile(int fd, struct mc_buf *mc)
{
int ret, cnt, i, contr;
struct pController *pc;
contr = CAPIMSG_U16(mc->rb, 8);
memset(&mc->rb[8], 0, 66);
CAPIMSG_SETSUBCOMMAND(mc->rb, CAPI_CONF);
CAPIMSG_SETLEN(mc->rb, 74);
if (mc->len < 10 || contr < 0) {
capimsg_setu16(mc->rb, 8, MIC_INFO_CODING_ERROR);
} else if (contr == 0) {
cnt = 0;
for (i = 0; i < mI_ControllerCount; i++) {
if (mI_Controller[i].enable)
cnt++;
}
capimsg_setu16(mc->rb, 8, CapiNoError);
capimsg_setu16(mc->rb, 10, cnt);
} else {
pc = get_cController(contr);
if (!pc) {
capimsg_setu16(mc->rb, 8, 0x2002); /* Illegal controller */
} else {
capimsg_setu16(mc->rb, 8, CapiNoError);
capimsg_setu16(mc->rb, 10, contr);
capimsg_setu16(mc->rb, 12, pc->profile.nbchannel);
capimsg_setu32(mc->rb, 14, pc->profile.goptions);
capimsg_setu32(mc->rb, 18, pc->profile.support1);
capimsg_setu32(mc->rb, 22, pc->profile.support2);
capimsg_setu32(mc->rb, 26, pc->profile.support3);
/* TODO reserved, manu */
}
}
ret = send(fd, mc->rb, 74, 0);
if (ret != 74)
eprint("error send %d/%d - %s\n", ret, 74, strerror(errno));
capi_dump_shared();
}
static void mIcapi_register(int fd, int idx, struct mc_buf *mc)
{
int ret;
uint16_t aid;
struct mApplication *appl;
uint32_t b3c, b3b, b3s;
CAPIMSG_SETSUBCOMMAND(mc->rb, CAPI_CONF);
aid = CAPIMSG_APPID(mc->rb);
if (mc->len == 20) {
iprint("register application id %d fd=%d\n", aid, fd);
b3c = CAPIMSG_U32(mc->rb, 8);
b3b = CAPIMSG_U32(mc->rb, 12);
b3s = CAPIMSG_U32(mc->rb, 16);
appl = RegisterApplication(aid, b3c, b3b, b3s);
if (appl) {
capimsg_setu16(mc->rb, 8, CapiNoError);
pollinfo[idx].type = PIT_Application;
pollinfo[idx].data = appl;
appl->fd = fd;
} else {
eprint("register application id %d fd %d failed\n", aid, fd);
capimsg_setu16(mc->rb, 8, CapiMsgOSResourceErr);
}
} else
capimsg_setu16(mc->rb, 8, MIC_INFO_CODING_ERROR);
ret = send(fd, mc->rb, 10, 0);
if (ret != 10)
eprint("error send %d/%d - %s\n", ret, 10, strerror(errno));
}
static void mIcapi_release(int fd, int idx, struct mc_buf *mc)
{
int ret;
uint16_t aid, info = CapiIllAppNr;
struct mApplication *appl = pollinfo[idx].data;
aid = CAPIMSG_APPID(mc->rb);
CAPIMSG_SETSUBCOMMAND(mc->rb, CAPI_CONF);
iprint("Unregister application %d (%d)\n", aid, appl ? appl->cobj.id2 : -1);
if (appl) {
if (aid == appl->cobj.id2) {
ShutdownAppl(idx, 1);
info = CapiNoError;
} else {
eprint("Application Id mismatch Got %d have %d\n", aid, appl->cobj.id2);
}
} else
eprint("Application not assigined\n");
capimsg_setu16(mc->rb, 8, info);
ret = send(fd, mc->rb, 10, 0);
if (ret != 10)
eprint("error send %d/%d - %s\n", ret, 10, strerror(errno));
if (info == CapiNoError) {
close(fd);
dprint(MIDEBUG_CONTROLLER, "close mIcapi_release fd %d\n", fd);
}
}
static void get_serial_number(int fd, struct mc_buf *mc)
{
int ret, contr = CAPIMSG_U16(mc->rb, 8);
CAPIMSG_SETSUBCOMMAND(mc->rb, CAPI_CONF);
capimsg_setu16(mc->rb, 8, CapiNoError);
/* only fake for now since we do not have serial numbers on most HW */
sprintf((char *)&mc->rb[10], "%07d", contr);
ret = send(fd, mc->rb, 18, 0);
if (ret != 18)
eprint("error send %d/%d - %s\n", ret, 18, strerror(errno));
}
static void get_capi_version(int fd, struct mc_buf *mc)
{
/* all the same for all cards */
int ret;
CAPIMSG_SETSUBCOMMAND(mc->rb, CAPI_CONF);
capimsg_setu16(mc->rb, 8, CapiNoError);
capimsg_setu32(mc->rb, 10, 2); /* major */
capimsg_setu32(mc->rb, 14, 0); /* minor */
capimsg_setu32(mc->rb, 18, 0); /* manu major */
capimsg_setu32(mc->rb, 22, 1); /* manu minor */
ret = send(fd, mc->rb, 26, 0);
if (ret != 26)
eprint("error send %d/%d - %s\n", ret, 26, strerror(errno));
}
static void get_manufacturer(int fd, struct mc_buf *mc)
{
/* We us a generic mISDN for now */
int ret;
CAPIMSG_SETSUBCOMMAND(mc->rb, CAPI_CONF);
memset(&mc->rb[8], 0, 66);
capimsg_setu16(mc->rb, 8, CapiNoError);
sprintf((char *)&mc->rb[10], "mISDN");
ret = send(fd, mc->rb, 74, 0);
if (ret != 74)
eprint("error send %d/%d - %s\n", ret, 74, strerror(errno));
}
static void misdn_manufacturer_req(int fd, struct mc_buf *mc)
{
/* nothing implemented, only return no error */
int ret;
CAPIMSG_SETSUBCOMMAND(mc->rb, CAPI_CONF);
capimsg_setu16(mc->rb, 8, CapiNoError);
ret = send(fd, mc->rb, 10, 0);
if (ret != 10)
eprint("error send %d/%d - %s\n", ret, 10, strerror(errno));
}
static void mIcapi_userflag(int fd, int idx, struct mc_buf *mc)
{
int ret;
struct mApplication *appl = pollinfo[idx].data;
uint32_t sf, cf, uf = 0xffffffff;
CAPIMSG_SETSUBCOMMAND(mc->rb, CAPI_CONF);
capimsg_setu16(mc->rb, 0, 12);
if (appl) {
uf = appl->UserFlags;
sf = CAPIMSG_U32(mc->rb, 8);
cf = CAPIMSG_U32(mc->rb, 12);
if (cf)
uf &= ~cf;
if (sf)
uf |= sf;
iprint("UserFlags old=%x new=%x\n", appl->UserFlags, uf);
appl->UserFlags = uf;
}
capimsg_setu32(mc->rb, 8, uf);
ret = send(fd, mc->rb, 12, 0);
if (ret != 12)
eprint("error send %d/%d - %s\n", ret, 12, strerror(errno));
}
static void mIcapi_ttyname(int fd, int idx, struct mc_buf *mc)
{
int ret, ml = 0, l = 0;
struct mApplication *appl = pollinfo[idx].data;
struct lPLCI *lp;
uint32_t ncci = 0;
char name[80];
CAPIMSG_SETSUBCOMMAND(mc->rb, CAPI_CONF);
if (appl) {
ncci = CAPIMSG_U32(mc->rb, 8);
ml = CAPIMSG_U32(mc->rb, 12);
if (appl->UserFlags & CAPIFLAG_HIGHJACKING) {
lp = get_lPLCI4plci(appl, ncci);
if (lp && lp->BIlink && lp->BIlink->tty > -1) {
ret = ptsname_r(lp->BIlink->tty, name, 80);
if (ret)
eprint("NCCI %06x: error to get ptsname for %d - %s\n",
ncci, lp->BIlink->tty, strerror(errno));
else
l = strlen(name);
} else
eprint("NCCI %06x: do not find lPLCI for NCCI\n", ncci);
}
}
if (l >= ml)
l = 0;
if (l) {
iprint("NCCI %06x: ttyname set to %s\n", ncci, name);
memcpy(&mc->rb[8], name, l);
} else
wprint("NCCI %06x: ttyname requested but not available\n", ncci);
mc->rb[8 + l] = 0;
capimsg_setu16(mc->rb, 0, l + 8);
ret = send(fd, mc->rb, l + 8, 0);
if (ret != l + 8)
eprint("error send %d/%d - %s\n", ret, 12, strerror(errno));
}
static int main_recv(int fd, int idx)
{
int ret, len, cmd, dl;
struct mc_buf *mc;
mc = alloc_mc_buf();
if (!mc)
return -ENOMEM;
ret = recv(fd, mc->rb, MC_RB_SIZE, MSG_DONTWAIT);
if (ret < 0) {
wprint("Error on reading from %d errno %d - %s\n", fd, errno, strerror(errno));
ret = -errno;
} else if (ret == 0) {
/* closed */
ret = -ECONNABORTED;
} else if (ret < 8) {
eprint("Short message read len %d (%02x%02x%02x%02x%02x%02x%02x%02x)\n",
ret, mc->rb[0], mc->rb[1], mc->rb[2], mc->rb[3], mc->rb[4], mc->rb[5], mc->rb[6], mc->rb[7]);
ret = -EBADMSG;
} else if (ret == MC_RB_SIZE) {
eprint("Message too big %d (%02x%02x%02x%02x%02x%02x%02x%02x)\n",
ret, mc->rb[0], mc->rb[1], mc->rb[2], mc->rb[3], mc->rb[4], mc->rb[5], mc->rb[6], mc->rb[7]);
ret = -EMSGSIZE;
}
if (ret < 0)
goto end;
len = CAPIMSG_LEN(mc->rb);
cmd = CAPIMSG_CMD(mc->rb);
if (cmd != CAPI_DATA_B3_REQ) {
if (len != ret) {
wprint("Msg len error on %04x read %d but indicated %d bytes\n", cmd, ret, len);
if (ret < len)
mc->len = ret;
}
mc->len = len;
} else {
dl = CAPIMSG_DATALEN(mc->rb);
if ((len + dl) != ret) {
wprint("Msg len error on DATA_B3_REQ msg len %d data len %d but read %d\n", len, dl, ret);
}
mc->len = ret;
}
switch (cmd) {
case MIC_GET_PROFILE_REQ:
get_profile(fd, mc);
break;
case MIC_REGISTER_REQ:
mIcapi_register(fd, idx, mc);
break;
case MIC_RELEASE_REQ:
mIcapi_release(fd, idx, mc);
break;
case MIC_SERIAL_NUMBER_REQ:
get_serial_number(fd, mc);
break;
case MIC_VERSION_REQ:
get_capi_version(fd, mc);
break;
case MIC_GET_MANUFACTURER_REQ:
get_manufacturer(fd, mc);
break;
case MIC_MANUFACTURER_REQ:
misdn_manufacturer_req(fd, mc);
break;
case MIC_USERFLAG_REQ:
mIcapi_userflag(fd, idx, mc);
break;
case MIC_TTYNAME_REQ:
mIcapi_ttyname(fd, idx, mc);
break;
default:
if (pollinfo[idx].type == PIT_Application)
ret = PutMessageApplication(pollinfo[idx].data, mc);
else {
wprint("CMD %x for fd=%d type %d not handled\n", cmd, fd, pollinfo[idx].type);
}
break;
}
end:
if (ret != 0) /* if message is not queued or freed */
free_mc_buf(mc);
return ret;
}
int main_loop(void)
{
int res, ret, i, idx, error = -1, timerId;
int running = 1, ShutDown = 0, polldelay;
int fd;
int nconn = -1;
struct sockaddr_un caddr;
char buf[4096];
socklen_t alen;
struct mApplication *appl;
ret = pipe(mIControl);
if (ret) {
eprint("error setup MasterControl pipe - %s\n", strerror(errno));
return errno;
} else
dprint(MIDEBUG_CONTROLLER, "create mIControl(%d, %d)\n", mIControl[0], mIControl[1]);
ret = add_mainpoll(mIControl[0], PIT_Control);
if (ret < 0) {
eprint("Error while adding mIControl to mainpoll (mainpoll_max %d)\n", mainpoll_max);
return -1;
} else
iprint("mIControl added to idx %d\n", ret);
ret = add_mainpoll(mIsock, PIT_mISDNmain);
if (ret < 0) {
eprint("Error while adding mIsock to mainpoll (mainpoll_max %d)\n", mainpoll_max);
return -1;
} else
iprint("mIsock added to idx %d\n", ret);
ret = add_mainpoll(mICAPItimer_base->tdev, PIT_mISDNtimer);
if (ret < 0) {
eprint("Error while adding mICAPItimer to mainpoll (mainpoll_max %d)\n", mainpoll_max);
return -1;
} else
iprint("mICAPItimer added to idx %d\n", ret);
pollinfo[ret].data = mICAPItimer_base;
ret = add_mainpoll(mCsock, PIT_CAPImain);
if (ret < 0) {
eprint("Error while adding mCsock to mainpoll (mainpoll_max %d)\n", mainpoll_max);
return -1;
} else
iprint("mCsock added to idx %d\n", ret);
polldelay = -1;
while (running) {
ret = poll(mainpoll, mainpoll_max, polldelay);
if (ret < 0) {
if (errno == EINTR)
iprint("Received signal - continue\n");
else {
wprint("Error on poll errno=%d - %s\n", errno, strerror(errno));
error = -errno;
}
continue;
}
if (ret == 0) { /* timeout */
if (ShutDown) {
eprint("Shutdown mainpoll timeout reached - force shutdown\n");
running = 0;
error = -EBUSY;
continue;
}
}
for (i = 0; i < mainpoll_max; i++) {
if (ret && mainpoll[i].revents) {
dprint(MIDEBUG_POLL, "Poll return %d for idx %d ev %x fd %d\n", ret,
i, mainpoll[i].revents, mainpoll[i].fd);
switch (pollinfo[i].type) {
case PIT_Bchannel:
if (mainpoll[i].revents & POLLIN) {
res = recvBchannel(pollinfo[i].data);
} else if (mainpoll[i].revents == POLLHUP) {
dprint(MIDEBUG_POLL, "Bchannel socket %d closed\n", mainpoll[i].fd);
ReleaseBchannel(pollinfo[i].data);
}
break;
case PIT_CAPImain: /* new connect */
if (mainpoll[i].revents & POLLIN) {
caddr.sun_family = AF_UNIX;
caddr.sun_path[0] = 0;
alen = sizeof(caddr);
nconn = accept(mCsock, (struct sockaddr *)&caddr, &alen);
if (nconn < 0) {
eprint("Error on accept - %s\n", strerror(errno));
} else {
dprint(MIDEBUG_POLL, "New connection on capisocket\n");
idx = add_mainpoll(nconn, PIT_NewConn);
if (idx < 0)
eprint("Cannot add fd=%d to mainpoll\n", nconn);
else
dprint(MIDEBUG_POLL, "nconn added to idx %d\n", idx);
}
}
break;
case PIT_mISDNmain:
res = read(mIsock, buf, 4096);
dprint(MIDEBUG_POLL, "Read %d from mIsock (%d)\n", res, mIsock);
break;
case PIT_Application:
if (mainpoll[i].revents == POLLHUP) {
dprint(MIDEBUG_POLL, "socket %d closed\n", mainpoll[i].fd);
ShutdownAppl(i, 0);
break;
}
/* no break to handle POLLIN */
case PIT_NewConn:
if (mainpoll[i].revents == POLLHUP) {
dprint(MIDEBUG_POLL, "socket %d closed\n", mainpoll[i].fd);
close(mainpoll[i].fd);
res = del_mainpoll(mainpoll[i].fd);
if (res < 0) {
eprint("Cannot delete fd=%d from mainpoll result %d\n",
mainpoll[i].fd, res);
}
}
res = main_recv(mainpoll[i].fd, i);
if (res == -ECONNABORTED || res == -ECONNRESET) {
if (pollinfo[i].type == PIT_Application) {
ShutdownAppl(i, 0);
break;
}
fd = mainpoll[i].fd;
dprint(MIDEBUG_POLL, "socket connection %s - fd %d idx %d type %d closed\n",
res == -ECONNABORTED ? "abort" : "reset", fd, i, pollinfo[i].type);
close(fd);
res = del_mainpoll(fd);
if (res < 0) {
eprint("Cannot delete fd=%d from mainpoll result %d\n", fd, res);
} else
dprint(MIDEBUG_POLL, "Deleted fd=%d from mainpoll\n", fd);
}
break;
case PIT_Control:
res = main_control(i);
if (res == MICD_CTRL_SHUTDOWN) {
iprint("Pollevent ShutdownRequest\n");
polldelay = MI_SHUTDOWN_DELAY;
ShutDown = 1;
res = ReleaseAllApplications();
if (res <= 0) { /* No Apllication or error shutdown now */
running = 0;
error = res;
}
}
break;
case PIT_ReleasedApp:
res = read(mainpoll[i].fd, &error, sizeof(error));
if (res == sizeof(error)) {
if (error == MI_PUT_APPLICATION) {
appl = pollinfo[i].data;
iprint("AppId %d Pollevent MI_PUT_APPLICATION refcnt:%d\n",
appl->cobj.id2, appl->cobj.refcnt);
if (appl->cobj.refcnt < 3) {
res = del_mainpoll(mainpoll[i].fd);
if (res < 0) {
eprint("Cannot delete fd=%d from mainpoll result %d\n",
mainpoll[i].fd, res);
}
Free_Application(&appl->cobj);
}
}
}
break;
case PIT_mISDNtimer:
res = read(mainpoll[i].fd, &timerId, sizeof(timerId));
if (res < 0) {
eprint("mISDN read timer error %s\n", strerror(errno));
} else {
if (res == sizeof(timerId) && timerId) {
expire_timer(pollinfo[i].data, timerId);
}
}
break;
default:
wprint("Unexpected poll event %x on fd %d type %d\n", mainpoll[i].revents,
mainpoll[i].fd, pollinfo[i].type);
break;
}
ret--;
}
if (ret == 0)
break;
}
}
clean_all();
return error;
}
static int my_lib_debug(const char *file, int line, const char *func, int level, const char *fmt, va_list va)
{
int ret, l;
char date[64], log[2048];
struct tm tm;
struct timeval tv;
pid_t tid = gettid();
char *LC = "UEWID";
ret = vsnprintf(log, 2000, fmt, va);
l = gettimeofday(&tv, NULL);
if (tv.tv_usec > 999999) {
tv.tv_sec++;
tv.tv_usec -= 1000000;
}
localtime_r(&tv.tv_sec, &tm);
l = strftime(date, sizeof(date), "%b %e %T", &tm);
snprintf(&date[l], sizeof(date) - l, ".%06d", (int)tv.tv_usec);
if (DebugFile) {
fprintf(DebugFile, "%s %c %16s:%4d %22s(%05d):%s", date, LC[level], file, line, func, tid, log);
fflush(DebugFile);
} else if (level > MISDN_LIBDEBUG_WARN)
fprintf(stdout, "%s %c %20s:%4d %22s(%05d):%s", date, LC[level], file, line, func, tid, log);
else
fprintf(stderr, "%s %c %20s:%4d %22s(%05d):%s", date, LC[level], file, line, func, tid, log);
return ret;
}
static int my_capilib_dbg(const char *file, int line, const char *func, const char *fmt, va_list va)
{
return my_lib_debug(file, line, func, 1, fmt, va);
}
static struct mi_ext_fn_s l3dbg = {
.prt_debug = my_lib_debug,
};
static struct sigaction mysig_term, mysig_dump, mysig_hup;
int main(int argc, char *argv[])
{
int ret, i, j, nb, c, exitcode = 1, ver, libdebug;
struct sockaddr_un mcaddr;
struct pController *pc;
struct group *grp;
mICAPItimer_base = &_mICAPItimer_base;
mICAPItimer_base->tdev = -1;
INIT_LIST_HEAD(&_mICAPItimer_base.pending_timer);
KeepTemporaryFiles = 0;
config_file = def_config;
ret = opt_parse(argc, argv);
if (ret)
exit(1);
memset(&mcaddr, 0, sizeof(mcaddr));
if (DebugFileName) {
DebugFile = fopen(DebugFileName, "w");
if (!DebugFile) {
fprintf(stderr, "Cannot open %s - %s\n", DebugFileName, strerror(errno));
exit(1);
}
}
libdebug = (debugmask & 0xff) << 24;
if (debugmask & 0x100)
libdebug |= 0xfffff;
register_dbg_vprintf(my_capilib_dbg);
ver = init_layer3(4, &l3dbg);
mISDN_set_debug_level(libdebug);
iprint("Init mISDN lib version %x, debug = %x (%x)\n", ver, debugmask, libdebug);
grp = getgrnam(MISDN_GROUP);
if (!grp) {
fprintf(stderr, "Cannot get %s group ID - %s\n", MISDN_GROUP, strerror(errno));
return 1;
} else {
if (setegid(grp->gr_gid) < 0) {
fprintf(stderr, "Cannot set effective group to %s gid:%d - %s\n",
MISDN_GROUP, (int)grp->gr_gid, strerror(errno));
return 1;
}
iprint("Did set eGID to %s gid:%d\n", MISDN_GROUP, (int)grp->gr_gid);
}
ret = mApplication_init();
if (ret) {
fprintf(stderr, "Cannot init mApplication -%s\n", strerror(ret));
return 1;
}
CAPIobj_init();
mc_buffer_init();
snprintf(_TempDirectory, 80, "%s/mISDNd_XXXXXX", MISDND_TEMP_DIR);
TempDirectory = mkdtemp(_TempDirectory);
if (!TempDirectory) {
fprintf(stderr, "Cannot create temporary directory %s - %s\n", _TempDirectory, strerror(errno));
return 1;
}
/* open mISDN */
/* test if /dev/mISDNtimer is accessible */
ret = open("/dev/mISDNtimer", O_RDWR);
if (ret < 0) {
fprintf(stderr, "Cannot access /dev/mISDNtimer - %s\n", strerror(errno));
return 1;
}
mICAPItimer_base->tdev = ret;
mIsock = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE);
if (mIsock < 0) {
fprintf(stderr, "mISDNv2 not installed - %s\n", strerror(errno));
return 1;
}
/* get number of stacks */
ret = ioctl(mIsock, IMGETCOUNT, &mI_ControllerCount);
if (ret < 0) {
fprintf(stderr, "mISDNv2 IMGETCOUNT error - %s\n", strerror(errno));
goto errout;
}
if (mI_ControllerCount < 1) {
fprintf(stderr, "mISDNv2 no controllers found\n");
goto errout;
}
mI_Controller = calloc(mI_ControllerCount, sizeof(*mI_Controller));
if (!mI_Controller) {
fprintf(stderr, "no memory to allocate %d controller struct (a %zd)\n",
mI_ControllerCount, sizeof(*mI_Controller));
goto errout;
}
for (i = 0; i < mI_ControllerCount; i++) {
pc = &mI_Controller[i];
pc->mNr = i;
pc->devinfo.id = i;
pc->enable = 1; /* default all controllers are enabled */
pc->L3Proto = L3_PROTOCOL_DSS1_USER;
pc->L3Flags = 0;
ret = init_cobj(&pc->cobjLC, NULL, Cot_Root, i, 0);
if (ret) {
fprintf(stderr, "Cannot init LC lock for controller %d ret:%d - %s\n", i + 1, ret, strerror(ret));
goto errout;
}
ret = init_cobj(&pc->cobjPLCI, NULL, Cot_Root, i, 0);
if (ret) {
fprintf(stderr, "Cannot init PLCI lock for controller %d ret:%d - %s\n", i + 1, ret, strerror(ret));
goto errout;
}
ret = ioctl(mIsock, IMGETDEVINFO, &pc->devinfo);
if (ret < 0) {
fprintf(stderr, "mISDNv2 IMGETDEVINFO error controller %d - %s\n", i + 1, strerror(errno));
goto errout;
}
c = 0;
nb = 0;
while (c <= MISDN_MAX_CHANNEL + 1) {
if (c <= MISDN_MAX_CHANNEL && test_channelmap(c, pc->devinfo.channelmap)) {
nb++;
pc->BImax = c;
}
c++;
}
pc->BImax++;
pc->BInstances = calloc(pc->BImax, sizeof(*pc->BInstances));
if (!pc->BInstances) {
fprintf(stderr, "no memory to allocate %d Bchannel instances for controller %d\n", pc->BImax, i + 1);
goto errout;
}
for (j = 0; j < pc->BImax; j++) {
pc->BInstances[j].nr = j;
pc->BInstances[j].pc = pc;
pc->BInstances[j].fd = -1;
pc->BInstances[j].tty = -1;
pc->BInstances[j].cpipe[0] = -1;
pc->BInstances[j].cpipe[1] = -1;
pthread_mutex_init(&pc->BInstances[j].lock, NULL);
sem_init(&pc->BInstances[j].wait, 0, 0);
}
pc->profile.ncontroller = i + 1;
pc->profile.nbchannel = nb;
pc->profile.goptions = 1; /* internal controller */
pc->profile.support1 = 0;
pc->profile.support2 = 0;
pc->profile.support3 = 0x01;
if (pc->devinfo.Bprotocols & (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK))) {
pc->profile.support1 |= 0x02;
pc->profile.support2 |= 0x02;
#ifdef USE_SOFTFAX
pc->profile.support1 |= 0x10;
pc->profile.support2 |= 0x10;
pc->profile.support3 |= 0x30;
#endif
}
if (pc->devinfo.Bprotocols & (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK))) {
pc->profile.support1 |= 0x01;
}
if (pc->devinfo.Bprotocols & (1 << (ISDN_P_B_X75SLP & ISDN_P_B_MASK))) {
pc->profile.support2 |= 0x01;
}
if (pc->devinfo.Bprotocols & (1 << (ISDN_P_B_L2DTMF & ISDN_P_B_MASK))) {
pc->profile.goptions |= 0x08;
}
if (pc->devinfo.Bprotocols & (1 << (ISDN_P_B_L2DSP & ISDN_P_B_MASK))) {
pc->profile.goptions |= 0x08;
}
if (pc->devinfo.Bprotocols & (1 << (ISDN_P_B_T30_FAX & ISDN_P_B_MASK))) {
pc->profile.support1 |= 0x10;
pc->profile.support2 |= 0x10;
pc->profile.support3 |= 0x10;
}
if (pc->devinfo.Bprotocols & (1 << (ISDN_P_B_MODEM_ASYNC & ISDN_P_B_MASK))) {
pc->profile.support1 |= 0x180;
pc->profile.support3 |= 0x80;
}
}
i = read_config_file(config_file);
if (i < 0)
goto errout;
retry_Csock:
mCsock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (mCsock < 0) {
fprintf(stderr, "cannot create socket - %s\n", strerror(errno));
goto errout;
}
mcaddr.sun_family = AF_UNIX;
ret = mkdir(MISDN_CAPI_SOCKET_DIR, S_IRWXU | S_IRWXG);
if (ret < 0) {
if (errno != EEXIST) {
fprintf(stderr, "cannot create socket directory %s - %s\n", MISDN_CAPI_SOCKET_DIR, strerror(errno));
goto errout;
} else
iprint("directory %s already exist - reusing it\n", MISDN_CAPI_SOCKET_DIR);
}
sprintf(mcaddr.sun_path, "%s/%s", MISDN_CAPI_SOCKET_DIR, MISDN_CAPI_SOCKET_NAME);
ret = bind(mCsock, (struct sockaddr *)&mcaddr, sizeof(mcaddr));
if (ret < 0) {
fprintf(stderr, "cannot bind socket to %s - %s\n", mcaddr.sun_path, strerror(errno));
if (errno == EADDRINUSE) { /* old socket file exist */
ret = connect(mCsock, (struct sockaddr *)&mcaddr, sizeof(mcaddr));
if (ret < 0) {
/* seems the socket file is not in use */
ret = unlink(mcaddr.sun_path);
if (ret < 0) {
fprintf(stderr, "cannot remove old socket file %s - %s\n",
mcaddr.sun_path, strerror(errno));
goto errout;
}
close(mCsock);
goto retry_Csock;
} else {
fprintf(stderr, "mISDNcapid is already running - only one instance can be used\n");
goto errout;
}
}
}
ret = chmod(mcaddr.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
if (ret < 0) {
fprintf(stderr, "cannot change permissions on unix socket:%s - %s\n", mcaddr.sun_path, strerror(errno));
goto errout;
}
if (listen(mCsock, 15)) {
fprintf(stderr, "cannot set listen mode for socket %s - %s\n", mcaddr.sun_path, strerror(errno));
goto errout;
}
fprintf(stderr, "debug=%#x do_daemon=%d config=%s\n", debugmask, do_daemon, config_file);
if (do_daemon) {
ret = daemon(0, 0);
if (ret < 0) {
fprintf(stderr, "cannot run as daemon\n");
goto errout;
}
}
init_listen();
init_lPLCI_fsm();
init_ncci_fsm();
#ifdef USE_SOFTFAX
create_lin2alaw_table();
g3_gen_tables();
#endif
/* setup signal handler */
memset(&mysig_term, 0, sizeof(mysig_term));
mysig_term.sa_handler = termHandler;
ret = sigaction(SIGTERM, &mysig_term, NULL);
if (ret) {
wprint("Error to setup signal handler for SIGTERM - %s\n", strerror(errno));
}
ret = sigaction(SIGINT, &mysig_term, NULL);
if (ret) {
wprint("Error to setup signal handler for SIGINT - %s\n", strerror(errno));
}
memset(&mysig_dump, 0, sizeof(mysig_dump));
mysig_dump.sa_handler = dumpHandler;
ret = sigaction(SIGUSR1, &mysig_dump, NULL);
if (ret) {
wprint("Error to setup signal handler for SIGUSR1 - %s\n", strerror(errno));
}
ret = sigaction(SIGUSR2, &mysig_dump, NULL);
if (ret) {
wprint("Error to setup signal handler for SIGUSR2 - %s\n", strerror(errno));
}
memset(&mysig_hup, 0, sizeof(mysig_hup));
mysig_hup.sa_handler = hupHandler;
ret = sigaction(SIGHUP, &mysig_hup, NULL);
if (ret) {
wprint("Error to setup signal handler for SIGHUP - %s\n", strerror(errno));
}
exitcode = main_loop();
free_ncci_fsm();
free_lPLCI_fsm();
free_listen();
#ifdef USE_SOFTFAX
g3_destroy_tables();
#endif
errout:
mc_buffer_cleanup();
cleanup_layer3();
CAPIobj_exit();
if (mCsock > -1)
close(mCsock);
close(mIsock);
if (mI_Controller)
free(mI_Controller);
if (mICAPItimer_base->tdev > 0)
close(mICAPItimer_base->tdev);
mICAPItimer_base->tdev = -1;
if (mcaddr.sun_path[0])
unlink(mcaddr.sun_path);
if (TempDirectory && !KeepTemporaryFiles) {
ret = rmdir(TempDirectory);
if (ret)
wprint("Error to remove TempDirectory:%s - %s\n", TempDirectory, strerror(errno));
else
iprint("Removed TempDirectory:%s\n", TempDirectory);
}
if (DebugFile)
fclose(DebugFile);
return exitcode;
}