summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2004-01-05 22:24:10 +0000
committerLennart Poettering <lennart@poettering.net>2004-01-05 22:24:10 +0000
commitd24a3f265ec4344b5502ec57df3cf8358f6f1499 (patch)
treeb869285f72044966ceaa4f6d2d7ab2cb998c03f3
parente44bcf6a233173911c2a52f314025abd28281485 (diff)
many changes
git-svn-id: file:///home/lennart/svn/public/ivam2/trunk@14 dbf6933d-3bce-0310-9bcc-ed052ba35b35
-rw-r--r--conf/msntab10
-rw-r--r--doc/TODO11
-rw-r--r--src/Makefile2
-rw-r--r--src/buffio.c276
-rw-r--r--src/buffio.h26
-rw-r--r--src/exec.c10
-rw-r--r--src/main.c41
-rw-r--r--src/modem.c213
-rw-r--r--src/modem.h6
-rw-r--r--src/msntab.c303
-rw-r--r--src/msntab.h17
11 files changed, 712 insertions, 203 deletions
diff --git a/conf/msntab b/conf/msntab
index ca16ef7..239eae4 100644
--- a/conf/msntab
+++ b/conf/msntab
@@ -1,8 +1,12 @@
-
# local MSN remote MSN options action
-41264179 41264179 rings=0 ivam-say /var/spool/ivam/hello.ulaw
+41264179 41264179 rings=0 ivam-play /var/spool/ivam/welcome.ulaw
41264179 41264177 rings=0 ivam-dialup --pin=4711 ppp0
* 41264179 rings=0 @hangup
41264179 * rings=2,shbuf ivam-voicebox --pin=4711
-* * defaults @ignore
+
+#46 36 defaults @hangup
+46 36 rings=0 ivam-play /var/spool/ivam/welcome.ulaw
+46 * defaults ivam-echo
+
+# $Id$
diff --git a/doc/TODO b/doc/TODO
index 18fe2b1..8686af0 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -1,7 +1,12 @@
-* implement msntab.c
-* implement shbuf support
* python part
-* dtmf fifo
+* implement shbuf support
* uid switching support
+* clean hangup on shutdown
+
+* implement msntab.c (DONE)
+* dtmf fifo (DONE)
+* env var passing (DONE)
+* msntab @include (DONE)
+* ring counter (DONE)
$Id$
diff --git a/src/Makefile b/src/Makefile
index 65ee382..c689956 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -2,7 +2,7 @@ CC=gcc
CFLAGS=`pkg-config --cflags libdaemon shbuf liboop` -Wall -O0 -pipe -g
LIBS=`pkg-config --libs libdaemon shbuf liboop` -Wall -O0 -pipe -g
-ivamd: modem.o main.o modemman.o buffio.o exec.o dle.o lock.o util.o msntab.o timevalarith.o
+ivamd: modem.o main.o modemman.o buffio.o exec.o dle.o lock.o util.o msntab.o timevalarith.o dtmffifo.o
$(CC) $(LIBS) -o $@ $^
clean:
diff --git a/src/buffio.c b/src/buffio.c
index 727821d..95db681 100644
--- a/src/buffio.c
+++ b/src/buffio.c
@@ -21,8 +21,8 @@ static void* buffio_timeout_cb(oop_source *source, struct timeval tv, void *user
static void* buffio_read_cb(oop_source *source, int fd, oop_event event, void *user);
static void* buffio_write_cb(oop_source *source, int fd, oop_event event, void *user);
-static void buffio_set_input_callbacks(struct buffio *b);
-static void buffio_set_output_callbacks(struct buffio *b);
+static void buffio_set_writable(struct buffio *b, int v);
+static void buffio_set_readable(struct buffio *b, int v);
struct buffio* buffio_new(int ifd, int ofd) {
struct buffio *b;
@@ -32,24 +32,21 @@ struct buffio* buffio_new(int ifd, int ofd) {
assert(b);
memset(b, 0, sizeof(struct buffio));
- b->ifd = ifd;
- b->ofd = ofd;
-
- daemon_nonblock(b->ifd, 1);
- daemon_nonblock(b->ofd, 1);
+ daemon_nonblock(b->ifd = ifd, 1);
+ daemon_nonblock(b->ofd = ofd, 1);
b->input_buf = malloc(b->input_max_length = BUFSIZE);
assert(b->input_buf);
b->input_length = b->input_index = 0;
- b->input_watermark = b->input_max_length; /* Read some more data if possible */
+ b->input_range = b->input_max_length;
b->output_buf = malloc(b->output_max_length = BUFSIZE);
assert(b->output_buf);
b->output_length = b->output_index = 0;
- b->output_watermark = 1; /* Write some data if 1 or more bytes are in the output buffer */
+ b->output_range = b->output_max_length;
- buffio_set_input_callbacks(b);
- buffio_set_output_callbacks(b);
+ buffio_set_readable(b, 0);
+ buffio_set_writable(b, 0);
return b;
}
@@ -114,6 +111,8 @@ static void* buffio_user_cb(oop_source *source, struct timeval tv, void *user) {
r |= b->input_ready_cb(b, b->user);
if (s & BUFFIO_SCHED_CB_OUTPUT_EMPTY && b->output_empty_cb && !b->output_length)
r |= b->output_empty_cb(b, b->user);
+ if (s & BUFFIO_SCHED_CB_OUTPUT_REQUEST && b->output_request_cb && b->output_length < b->output_range)
+ r |= b->output_request_cb(b, b->user);
if (s & BUFFIO_SCHED_CB_EOF && b->eof_cb)
r |= b->eof_cb(b, b->user);
if (s & BUFFIO_SCHED_CB_EPIPE && b->epipe_cb)
@@ -124,9 +123,9 @@ static void* buffio_user_cb(oop_source *source, struct timeval tv, void *user) {
return r == 0 ? OOP_CONTINUE : OOP_HALT;
}
-
static void buffio_sched_cb(struct buffio *b, enum buffio_sched_cb s) {
if (s & BUFFIO_SCHED_CB_INPUT_READY && !b->input_ready_cb) s &= ~BUFFIO_SCHED_CB_INPUT_READY;
+ if (s & BUFFIO_SCHED_CB_OUTPUT_REQUEST && !b->output_request_cb) s &= ~BUFFIO_SCHED_CB_OUTPUT_REQUEST;
if (s & BUFFIO_SCHED_CB_OUTPUT_EMPTY && !b->output_empty_cb) s &= ~BUFFIO_SCHED_CB_OUTPUT_EMPTY;
if (s & BUFFIO_SCHED_CB_EOF && !b->eof_cb) s &= ~BUFFIO_SCHED_CB_EOF;
if (s & BUFFIO_SCHED_CB_EPIPE && !b->epipe_cb) s = ~BUFFIO_SCHED_CB_EPIPE;
@@ -170,13 +169,13 @@ inline static void buffio_normalize(struct buffio *b) {
b->output_index = 0;
}
-static void buffio_set_input_callbacks(struct buffio *b) {
+static void buffio_set_readable(struct buffio *b, int v) {
assert(b && event_source);
if (b->ifd == -1)
return;
- if (!b->readable && b->input_length < b->input_watermark) {
+ if (!(b->readable = v)) {
if (!b->b_read_cb) { /* Enable the callback */
event_source->on_fd(event_source, b->ifd, OOP_READ, buffio_read_cb, b);
b->b_read_cb = 1;
@@ -189,13 +188,13 @@ static void buffio_set_input_callbacks(struct buffio *b) {
}
}
-static void buffio_set_output_callbacks(struct buffio *b) {
+static void buffio_set_writable(struct buffio *b, int v) {
assert(b && event_source);
if (b->ofd == -1)
return;
- if (!b->writable && b->output_length >= b->output_watermark) {
+ if (!(b->writable = v)) {
if (!b->b_write_cb) { /* Enable the callback */
event_source->on_fd(event_source, b->ofd, OOP_WRITE, buffio_write_cb, b);
b->b_write_cb = 1;
@@ -243,108 +242,109 @@ void buffio_set_delay_usec(struct buffio *b, unsigned long delay, unsigned long
}
static void do_read(struct buffio *b) {
+ ssize_t s;
+ size_t m, i;
+
assert(b);
- if (b->readable && b->input_length < b->input_watermark) {
- ssize_t s;
- size_t m, i;
-
- i = (b->input_index + b->input_length) % b->input_max_length;
-
- m = b->input_max_length-b->input_length;
- if (m > b->input_max_length-i)
- m = b->input_max_length-i;
-
- s = read(b->ifd, b->input_buf+i, m);
- b->readable = 0;
-
- //daemon_log(LOG_INFO, "%p: Read %u (%u) bytes.", b, s, m);
-
- if (s < 0) {
- daemon_log(LOG_ERR, "Failed to read from file descriptor: %s", strerror(errno));
- buffio_close_input_fd(b);
- buffio_sched_cb(b, BUFFIO_SCHED_CB_ERROR);
- return;
-
- } else if (!s) {
- buffio_close_input_fd(b);
- buffio_sched_cb(b, BUFFIO_SCHED_CB_EOF);
- return;
-
- } else {
+ if (!b->readable || b->input_length >= b->input_range || b->ifd == -1)
+ return;
+
+ i = (b->input_index + b->input_length) % b->input_max_length;
+ m = b->input_range-b->input_length;
+ if (m > b->input_max_length-i)
+ m = b->input_max_length-i;
+
+ s = read(b->ifd, b->input_buf+i, m);
+ //daemon_log(LOG_INFO, "%p: Read %u (%u) bytes.", b, s, m);
+
+ if (s < 0) {
+ daemon_log(LOG_ERR, "Failed to read from file descriptor: %s", strerror(errno));
+ buffio_close_input_fd(b);
+ buffio_sched_cb(b, BUFFIO_SCHED_CB_ERROR);
+ return;
+
+ } else if (!s) {
+ buffio_close_input_fd(b);
+ buffio_sched_cb(b, BUFFIO_SCHED_CB_EOF);
+ return;
+
+ }
+
#ifdef IODEBUG
- esc_print(b, "INPUT: ", b->input_buf+i, s);
+ esc_print(b, "INPUT: ", b->input_buf+i, s);
#endif
-
- assert(s <= m);
- b->input_length += s;
- buffio_normalize(b);
- }
- }
- buffio_set_input_callbacks(b);
+ assert(s <= m);
+ b->input_length += s;
+ buffio_normalize(b);
+
+ buffio_set_readable(b, 0);
- if (b->input_length)
+ if (b->input_length)
buffio_sched_cb(b, BUFFIO_SCHED_CB_INPUT_READY);
-
- return;
}
static void do_write(struct buffio *b) {
- assert(b);
+ ssize_t s;
+ size_t m;
- if (!b->delaying && b->writable && b->output_length >= b->output_watermark) {
- ssize_t s;
- size_t m;
+ assert(b);
- m = b->output_length;
- if (m > b->output_max_length-b->output_index)
- m = b->output_max_length-b->output_index;
+ if (b->delaying || !b->writable || !b->output_length || b->ofd == -1)
+ return;
- s = write(b->ofd, b->output_buf+b->output_index, m);
- b->writable = 0;
+ if (b->prebuf && b->output_length >= b->output_range)
+ b->prebuf = 0;
- //daemon_log(LOG_INFO, "%p: Wrote %u (%u) bytes.", b, s, m);
+ if (b->prebuf)
+ return;
+
+ m = b->output_length;
+ if (m > b->output_max_length-b->output_index)
+ m = b->output_max_length-b->output_index;
+
+ s = write(b->ofd, b->output_buf+b->output_index, m);
+ //daemon_log(LOG_INFO, "%p: Wrote %u (%u) bytes.", b, s, m);
+
+ if (s < 0) {
+ buffio_close_output_fd(b);
- if (s < 0) {
- buffio_close_output_fd(b);
+ if (errno == EPIPE) {
+ buffio_sched_cb(b, BUFFIO_SCHED_CB_EPIPE);
+ return;
- if (errno == EPIPE) {
- buffio_sched_cb(b, BUFFIO_SCHED_CB_EPIPE);
- return;
-
- } else {
- daemon_log(LOG_ERR, "Failed to write to file descriptor: %s", strerror(errno));
- buffio_sched_cb(b, BUFFIO_SCHED_CB_ERROR);
- return;
- }
+ } else {
+ daemon_log(LOG_ERR, "Failed to write to file descriptor: %s", strerror(errno));
+ buffio_sched_cb(b, BUFFIO_SCHED_CB_ERROR);
+ return;
}
-
+ }
+
#ifdef IODEBUG
- esc_print(b, "OUTPUT: ", b->output_buf+b->output_index, s);
+ esc_print(b, "OUTPUT: ", b->output_buf+b->output_index, s);
#endif
+
+ assert(s > 0 && s <= m);
+ b->output_index = (b->output_index + s) % b->output_max_length;
+ b->output_length -= s;
+ buffio_normalize(b);
+
+ buffio_set_writable(b, 0);
+ buffio_delay(b, s);
- assert(s > 0 && s <= m);
- b->output_index = (b->output_index + s) % b->output_max_length;
- b->output_length -= s;
-
- buffio_normalize(b);
- buffio_delay(b, s);
-
- if (!b->output_length)
- buffio_sched_cb(b, BUFFIO_SCHED_CB_OUTPUT_EMPTY);
- } else
- buffio_set_output_callbacks(b);
-
- return;
+ if (b->output_length < b->output_range)
+ buffio_sched_cb(b, BUFFIO_SCHED_CB_OUTPUT_REQUEST);
+ if (!b->output_length)
+ buffio_sched_cb(b, BUFFIO_SCHED_CB_OUTPUT_EMPTY);
}
static void* buffio_read_cb(oop_source *source, int fd, oop_event event, void *user) {
struct buffio *b = user;
assert(source && b && b->ifd == fd && event == OOP_READ);
- b->readable = 1;
+ buffio_set_readable(b, 1);
do_read(b);
return OOP_CONTINUE;
@@ -354,7 +354,7 @@ static void* buffio_write_cb(oop_source *source, int fd, oop_event event, void *
struct buffio *b = user;
assert(source && b && b->ofd == fd && event == OOP_WRITE);
- b->writable = 1;
+ buffio_set_writable(b, 1);
do_write(b);
return OOP_CONTINUE;
@@ -370,12 +370,12 @@ static void* buffio_timeout_cb(oop_source *source, struct timeval tv, void *user
return OOP_CONTINUE;
}
-int buffio_write(struct buffio *b, const uint8_t *d, size_t l) {
+void buffio_write(struct buffio *b, const uint8_t *d, size_t l) {
assert(b && d && l);
- if (l > b->output_max_length - b->output_length) {
+ if (b->output_length > b->output_range || l > b->output_range - b->output_length) {
daemon_log(LOG_ERR, "buffio_write() with too much data called");
- return -1;
+ return;
}
while (l > 0) {
@@ -394,38 +394,34 @@ int buffio_write(struct buffio *b, const uint8_t *d, size_t l) {
}
buffio_normalize(b);
-
do_write(b);
- return 0;
}
-int buffio_print(struct buffio *b, const char *s) {
+void buffio_print(struct buffio *b, const char *s) {
assert(b && s);
- return buffio_write(b, (uint8_t*) s, strlen(s));
+ buffio_write(b, (uint8_t*) s, strlen(s));
}
-int buffio_command(struct buffio *b, const char *c) {
+void buffio_command(struct buffio *b, const char *c) {
assert(b && c);
buffio_flush_input(b);
- return buffio_print(b, c);
+ buffio_print(b, c);
}
void buffio_flush_input(struct buffio *b) {
assert(b);
b->input_length = b->input_index = 0;
- buffio_set_input_callbacks(b);
+ do_read(b);
}
void buffio_flush_output(struct buffio *b) {
assert(b);
b->output_length = b->output_index = 0;
- buffio_set_output_callbacks(b);
}
-
int buffio_find_input(struct buffio *b, const char *c) {
size_t l, cl, i;
assert(b && c && *c);
@@ -441,7 +437,6 @@ int buffio_find_input(struct buffio *b, const char *c) {
if (!*p) { /* Found! */
b->input_index = j;
b->input_length = l-cl;
-
buffio_normalize(b);
do_read(b);
@@ -472,7 +467,6 @@ char* buffio_read_line(struct buffio *b, char *c, size_t l) {
b->input_index = (b->input_index+j+1) % b->input_max_length;
b->input_length -= j+1;
-
buffio_normalize(b);
do_read(b);
@@ -504,34 +498,39 @@ void buffio_dump(struct buffio *b) {
fprintf(stderr, "]\n");
}
-void buffio_set_input_watermark(struct buffio *b, ssize_t w) {
+void buffio_set_input_range(struct buffio *b, ssize_t w) {
assert(b);
if (w < 0)
- b->input_watermark = b->input_max_length;
+ b->input_range = b->input_max_length;
else
- b->input_watermark = w;
+ b->input_range = w;
+
+ assert(b->input_range > 0 && b->input_range <= b->input_max_length);
- assert(b->input_watermark > 0 && b->input_watermark <= b->input_max_length);
- buffio_set_input_callbacks(b);
+ do_read(b);
}
-void buffio_set_output_watermark(struct buffio *b, ssize_t w) {
+void buffio_set_output_range(struct buffio *b, ssize_t w) {
assert(b);
if (w < 0)
- b->output_watermark = b->output_max_length;
+ b->output_range = b->output_max_length;
else
- b->output_watermark = w;
+ b->output_range = w;
- assert(b->output_watermark > 0 && b->output_watermark <= b->output_max_length);
- buffio_set_output_callbacks(b);
+ assert(b->output_range > 0 && b->output_range <= b->output_max_length);
+
+ do_write(b);
}
const uint8_t* buffio_read_ptr(struct buffio *b, size_t *l) {
assert(b && l);
+ if (!b->input_length)
+ return NULL;
+
*l = b->input_length;
if (*l > b->input_max_length - b->input_index)
@@ -553,7 +552,6 @@ void buffio_read_ptr_inc(struct buffio *b, size_t l) {
b->input_index -= b->input_max_length;
b->input_length -= l;
-
buffio_normalize(b);
do_read(b);
@@ -563,9 +561,12 @@ uint8_t* buffio_write_ptr(struct buffio *b, size_t *l) {
size_t j;
assert(b && l);
+ if (b->output_length >= b->output_range)
+ return NULL;
+
j = (b->output_index + b->output_length) % b->output_max_length;
- *l = b->output_max_length - b->output_length;
+ *l = b->output_range - b->output_length;
if (*l > b->output_max_length - j)
*l = b->output_max_length - j;
@@ -583,7 +584,6 @@ void buffio_write_ptr_inc(struct buffio *b, size_t l) {
assert(l <= b->output_max_length - b->output_length && l <= b->output_max_length - j);
b->output_length += l;
-
buffio_normalize(b);
do_write(b);
@@ -595,6 +595,12 @@ int buffio_output_is_empty(struct buffio *b) {
return b->output_length == 0;
}
+int buffio_input_is_full(struct buffio *b) {
+ assert(b);
+
+ return b->input_length >= b->input_range;
+}
+
void buffio_dump_lines(struct buffio *b) {
int r, i;
assert(b);
@@ -608,9 +614,8 @@ void buffio_dump_lines(struct buffio *b) {
if (r > 0) {
b->input_index = (b->input_index+r) % b->input_max_length;
b->input_length -= r;
-
buffio_normalize(b);
-
+
do_read(b);
}
}
@@ -618,20 +623,31 @@ void buffio_dump_lines(struct buffio *b) {
void buffio_set_fds(struct buffio *b, int ifd, int ofd) {
assert(b && b->ifd == -1 && b->ofd == -1);
- b->ifd = ifd;
- b->ofd = ofd;
+ daemon_nonblock(b->ifd = ifd, 1);
+ daemon_nonblock(b->ofd = ofd, 1);
- daemon_nonblock(b->ifd, 1);
- daemon_nonblock(b->ofd, 1);
+ buffio_set_readable(b, 0);
+ buffio_set_writable(b, 0);
+}
- b->readable = b->writable = 0;
+int buffio_can_write(struct buffio *b, size_t l) {
+ assert(b);
+
+ if (b->output_length >= b->output_range)
+ return 0;
- buffio_set_input_callbacks(b);
- buffio_set_output_callbacks(b);
+ return l <= b->output_range - b->output_length;
}
-int buffio_can_write(struct buffio *b, size_t l) {
+int buffio_write_req(struct buffio *b) {
assert(b);
- return l <= b->output_max_length - b->output_length;
+ return b->output_length < b->output_range;
+}
+
+void buffio_set_prebuf(struct buffio *b, int p) {
+ assert(b);
+
+ b->prebuf = p;
+ do_write(b);
}
diff --git a/src/buffio.h b/src/buffio.h
index a41ec76..fff2514 100644
--- a/src/buffio.h
+++ b/src/buffio.h
@@ -7,7 +7,8 @@
enum buffio_sched_cb {
BUFFIO_SCHED_CB_IDLE = 0,
BUFFIO_SCHED_CB_INPUT_READY = 1,
- BUFFIO_SCHED_CB_OUTPUT_EMPTY = 2,
+ BUFFIO_SCHED_CB_OUTPUT_REQUEST = 2,
+ BUFFIO_SCHED_CB_OUTPUT_EMPTY = 32,
BUFFIO_SCHED_CB_EOF = 4,
BUFFIO_SCHED_CB_EPIPE = 8,
BUFFIO_SCHED_CB_ERROR = 16
@@ -18,19 +19,20 @@ struct buffio {
int ofd;
uint8_t *input_buf;
- size_t input_max_length, input_index, input_length, input_watermark;
+ size_t input_max_length, input_index, input_length, input_range;
uint8_t *output_buf;
- size_t output_max_length, output_index, output_length, output_watermark;
+ size_t output_max_length, output_index, output_length, output_range;
int b_read_cb, b_write_cb;
void *user;
- int readable;
- int writable;
+ int readable, writable;
+ int prebuf;
int (*input_ready_cb) (struct buffio *b, void *user);
+ int (*output_request_cb) (struct buffio *b, void *user);
int (*output_empty_cb) (struct buffio *b, void *user);
int (*eof_cb) (struct buffio *b, void *user);
int (*epipe_cb) (struct buffio *b, void *user);
@@ -47,20 +49,20 @@ struct buffio {
struct buffio* buffio_new(int ifd, int ofd);
void buffio_free(struct buffio *b);
-int buffio_write(struct buffio *b, const uint8_t *d, size_t l);
-int buffio_print(struct buffio *b, const char *s);
+void buffio_write(struct buffio *b, const uint8_t *d, size_t l);
+void buffio_print(struct buffio *b, const char *s);
void buffio_flush_input(struct buffio *b);
void buffio_flush_output(struct buffio *b);
-int buffio_command(struct buffio *b, const char *c);
+void buffio_command(struct buffio *b, const char *c);
int buffio_find_input(struct buffio *b, const char *c);
char *buffio_read_line(struct buffio *b, char *c, size_t l);
void buffio_dump(struct buffio *b);
-void buffio_set_input_watermark(struct buffio *b, ssize_t w);
-void buffio_set_output_watermark(struct buffio *b, ssize_t w);
+void buffio_set_input_range(struct buffio *b, ssize_t w);
+void buffio_set_output_range(struct buffio *b, ssize_t w);
const uint8_t* buffio_read_ptr(struct buffio *b, size_t *l);
void buffio_read_ptr_inc(struct buffio *b, size_t l);
@@ -72,6 +74,8 @@ void buffio_close_input_fd(struct buffio *b);
void buffio_close_output_fd(struct buffio *b);
int buffio_output_is_empty(struct buffio *b);
+int buffio_input_is_full(struct buffio *b);
+
void buffio_dump_lines(struct buffio *b);
void buffio_set_fds(struct buffio *b, int ifd, int ofd);
@@ -80,4 +84,6 @@ void buffio_set_delay_usec(struct buffio *b, unsigned long usec, unsigned long l
int buffio_can_write(struct buffio *b, size_t l);
+void buffio_set_prebuf(struct buffio *b, int p);
+
#endif
diff --git a/src/exec.c b/src/exec.c
index d9195c6..fabe757 100644
--- a/src/exec.c
+++ b/src/exec.c
@@ -164,7 +164,7 @@ static void *oop_read_cb(oop_source *source, int fd, oop_event event, void *user
/* Escape */
for (c = start, i = 0; i < s; i++, c++)
- if (*c < 32 || *c == 127)
+ if (*c != '\r' && *c != '\n' && (*c < 32 || *c == 127))
*c = '.';
@@ -197,6 +197,8 @@ pid_t child_process_create(const char *file, char *const argv[], int *ifd, int *
int stdout_fds[2];
int stderr_fds[2];
+ daemon_log(LOG_INFO, "Executing child process '%s'.", file);
+
if (pipe(stdin_fds) < 0) {
daemon_log(LOG_ERR, "pipe() failed: %s", strerror(errno));
return -1;
@@ -296,9 +298,9 @@ pid_t child_process_create(const char *file, char *const argv[], int *ifd, int *
exit(1);
}
}
-
- execv(file, argv);
- daemon_log(LOG_ERR, "exec() failed: %s", strerror(errno));
+
+ execvp(file, argv);
+ daemon_log(LOG_ERR, "exec('%s', ...) failed: %s", file, strerror(errno));
exit(1);
}
}
diff --git a/src/main.c b/src/main.c
index 9710be9..4f5a273 100644
--- a/src/main.c
+++ b/src/main.c
@@ -5,22 +5,45 @@
#include "main.h"
#include "exec.h"
#include "modemman.h"
+#include "msntab.h"
#define CHANNELS 1
oop_source* event_source = NULL;
+#define MSNTABLE "../conf/msntab"
+
static void *oop_exit_cb(oop_source *source, int sig, void *user) {
daemon_log(LOG_ERR, "Recieved signal %s", sig == SIGINT ? "SIGINT" : (sig == SIGTERM ? "SIGTERM" : "UNKNWON"));
return OOP_HALT;
}
+static void *oop_reload_cb(oop_source *source, int sig, void *user) {
+ daemon_log(LOG_ERR, "Reloading MSN table.");
+ msntab_flush();
+
+ if (msntab_load(MSNTABLE) < 0) {
+ daemon_log(LOG_ERR, "Ignoring all calls.");
+ msntab_flush();
+ }
+
+ return OOP_CONTINUE;
+}
+
+static void *oop_dump_cb(oop_source *source, int sig, void *user) {
+ msntab_dump();
+ return OOP_CONTINUE;
+}
+
+
int main_loop(void) {
int r = -1;
oop_source_sys *sys = NULL;
+ daemon_log(LOG_INFO, "Starting up.");
+
if (!(sys = oop_sys_new())) {
- daemon_log(LOG_ERR, "Failed to create system source");
+ daemon_log(LOG_ERR, "Failed to create system source.");
goto finish;
}
@@ -30,12 +53,19 @@ int main_loop(void) {
if (child_process_init() < 0)
goto finish;
+ if (msntab_load(MSNTABLE) < 0)
+ goto finish;
+
if (modem_manager_init(CHANNELS) < 0)
goto finish;
event_source->on_signal(event_source, SIGINT, oop_exit_cb, NULL);
event_source->on_signal(event_source, SIGTERM, oop_exit_cb, NULL);
+ event_source->on_signal(event_source, SIGHUP, oop_reload_cb, NULL);
+ event_source->on_signal(event_source, SIGUSR1, oop_dump_cb, NULL);
signal(SIGPIPE, SIG_IGN);
+
+ daemon_log(LOG_INFO, "Start up complete.");
if (oop_sys_run(sys) == OOP_ERROR) {
daemon_log(LOG_ERR, "oop_sys_new() returned OOP_ERROR");
@@ -45,8 +75,15 @@ int main_loop(void) {
r = 0;
finish:
+
+ daemon_log(LOG_INFO, "Shutting down.");
+
event_source->cancel_signal(event_source, SIGTERM, oop_exit_cb, NULL);
event_source->cancel_signal(event_source, SIGINT, oop_exit_cb, NULL);
+ event_source->cancel_signal(event_source, SIGHUP, oop_reload_cb, NULL);
+ event_source->cancel_signal(event_source, SIGUSR1, oop_dump_cb, NULL);
+
+ msntab_flush();
modem_manager_done();
child_process_done();
@@ -55,6 +92,8 @@ finish:
event_source = NULL;
oop_sys_delete(sys);
}
+
+ daemon_log(LOG_INFO, "Shut down complete.");
return r;
}
diff --git a/src/modem.c b/src/modem.c
index 71a8eb4..ef79b20 100644
--- a/src/modem.c
+++ b/src/modem.c
@@ -10,6 +10,7 @@
#include <time.h>
#include <stdio.h>
#include <sys/wait.h>
+#include <stdlib.h>
#include <oop.h>
@@ -21,22 +22,31 @@
#include "main.h"
#include "msntab.h"
#include "dle.h"
+#include "dtmffifo.h"
/* Baudrate for the TTY. Should be greater the 64000. */
#define BAUD_RATE B115200
-#define RESET_IDLE_TIME 10
+#define RESET_IDLE_TIME 2
+#define RING_TIME 5
+
#define INIT_TIMEOUT 5
#define ACCEPT_TIMEOUT 5
#define BASIC_TIMEOUT 5
#define CHILD_TIMEOUT 10
#define HANGUP_TIMEOUT 10
-#define ISDN_DELAY_USEC 125L
-#define ISDN_LATENCY_USEC 25000L
-#define INPUT_WATERMARK_DEFAULT -1
-#define INPUT_WATERMARK_CONNECTION 2
-#define INPUT_WATERMARK_CHILD 1
+#define ISDN_DELAY_USEC 125L /* The time a single byte written to the ISDN takes to be played */
+#define ISDN_LATENCY_USEC 50000L /* Request the next byte to be written to the ISDN this many usecs before the last one is finished playing */
+
+#define INPUT_RANGE_DEFAULT -1
+#define OUTPUT_RANGE_DEFAULT -1
+
+#define INPUT_RANGE_CONNECTION 96
+#define OUTPUT_RANGE_CONNECTION 1024
+
+#define INPUT_RANGE_CHILD 64
+#define OUTPUT_RANGE_CHILD 1024
#define INIT_AT_COMMANDS 16
static const char* const init_at_commands[INIT_AT_COMMANDS*2] = {
@@ -67,7 +77,7 @@ static const char* const init_at_commands[INIT_AT_COMMANDS*2] = {
"ATS13.0=1\n", /* direct tty send */
"OK\r\n",
- "AT\n", //S13.2=0\n", /* Don't hangup on DTR low */
+ "ATS13.6=1\n", /* Get special RUNG messages */
"OK\r\n",
"ATS23=1\n",
@@ -92,6 +102,7 @@ static const char* const init_at_commands[INIT_AT_COMMANDS*2] = {
static const char hup_sequence[] = { DLE, DC4, DLE, ETX, 0 };
static const char ath_sequence[] = "\nATH\n";
+static int modem_output_request_cb(struct buffio *b, void *user);
static int modem_output_empty_cb(struct buffio *b, void *user);
static int modem_input_ready_cb(struct buffio *b, void *user);
static void* oop_timeout_cb(oop_source *source, struct timeval tv, void *user);
@@ -105,12 +116,12 @@ static int modem_reopen(struct modem *m);
void modem_close(struct modem *m);
struct modem *modem_open(const char *dev) {
- struct modem *m;
+ struct modem *m = NULL;
assert(dev);
if (device_lock(dev) != 0)
- return NULL;
+ goto fail;
m = malloc(sizeof(struct modem));
assert(m);
@@ -121,7 +132,7 @@ struct modem *modem_open(const char *dev) {
m->child_pid = -1;
m->child_buffio = NULL;
- m->local_msn = "46";
+ m->listen_msn = "46";
m->tabentry = NULL;
if (modem_reopen(m) < 0)
@@ -132,7 +143,9 @@ struct modem *modem_open(const char *dev) {
return m;
fail:
- modem_close(m);
+ if (m)
+ modem_close(m);
+
return NULL;
}
@@ -140,8 +153,13 @@ fail:
void modem_close(struct modem *m) {
assert(m);
+ daemon_log(LOG_INFO, "Closing modem on TTY device '%s'", m->dev);
+
modem_timeout(m, 0);
+ if (m->dtmf_fifo)
+ dtmf_fifo_free(m->dtmf_fifo);
+
if (m->tabentry)
msntab_unref(m->tabentry);
@@ -196,7 +214,7 @@ static void modem_next_command(struct modem *m) {
if (m->command_index == 0)
buffio_command(m->buffio, hup_sequence);
else if (m->command_index == 15) {
- snprintf(tmp, sizeof(tmp), p, m->local_msn);
+ snprintf(tmp, sizeof(tmp), p, m->listen_msn);
p = tmp;
}
@@ -277,8 +295,10 @@ static void modem_hangup(struct modem *m, int b) {
assert(m);
daemon_log(LOG_INFO, "Hanging up.");
- buffio_set_input_watermark(m->buffio, INPUT_WATERMARK_DEFAULT);
+ buffio_set_input_range(m->buffio, INPUT_RANGE_DEFAULT);
+ buffio_set_output_range(m->buffio, OUTPUT_RANGE_DEFAULT);
buffio_set_delay_usec(m->buffio, 0, 0);
+ buffio_set_prebuf(m->buffio, 0);
l = strlen(ath_sequence) + (b ? strlen(hup_sequence) : 0);
@@ -295,6 +315,17 @@ static void modem_hangup(struct modem *m, int b) {
modem_timeout(m, HANGUP_TIMEOUT);
}
+static void modem_accept(struct modem *m) {
+ assert(m);
+
+ daemon_log(LOG_INFO, "Accepting call.");
+
+ m->state = MODEM_STATE_ATA;
+ modem_timeout(m, ACCEPT_TIMEOUT);
+
+ buffio_command(m->buffio, "ATA\n");
+}
+
static void child_exit_cb(pid_t t, int status, void *user) {
struct modem *m = user;
assert(m && m->child_pid == t);
@@ -307,6 +338,10 @@ static void child_exit_cb(pid_t t, int status, void *user) {
daemon_log(LOG_ERR, "Child exited due to unknown cause.");
m->child_pid = (pid_t) -1;
+ if (m->dtmf_fifo) {
+ dtmf_fifo_free(m->dtmf_fifo);
+ m->dtmf_fifo = NULL;
+ }
switch (m->state) {
@@ -323,9 +358,10 @@ static int child_input_ready_cb(struct buffio *b, void *user) {
struct modem *m = (struct modem*) user;
assert(m && m->child_buffio == b);
- if (m->state == MODEM_STATE_CONNECTION)
+ if (m->state == MODEM_STATE_CONNECTION) {
+ //daemon_log(LOG_INFO, "child_input_ready");
copy_child_to_modem(m);
- else
+ } else
buffio_flush_input(m->child_buffio);
return 0;
@@ -380,7 +416,7 @@ static int child_epipe_cb(struct buffio *b, void *user) {
return 0;
}
-static int child_output_empty_cb(struct buffio *b, void *user) {
+static int child_output_request_cb(struct buffio *b, void *user) {
struct modem *m = user;
assert(m && m->child_buffio == b);
@@ -405,18 +441,34 @@ static int modem_start_child(struct modem *m) {
int ifd, ofd;
assert(m && m->child_pid == (pid_t) -1 && m->tabentry && m->state == MODEM_STATE_CONNECTION);
+ assert(!m->dtmf_fifo);
+ if (!(m->dtmf_fifo = dtmf_fifo_new()))
+ return -1;
+
+ setenv("RINGMSN", m->ring_number ? m->ring_number : "", 1);
+ setenv("CALLERMSN", m->caller_number ? m->caller_number : "", 1);
+ assert(m->dtmf_fifo->fname);
+ setenv("DTMFFIFO", m->dtmf_fifo->fname, 1);
+ if (m->listen_msn)
+ setenv("LISTENMSN", m->listen_msn, 1);
+ else
+ unsetenv("LISTENMSN");
+
assert(m->tabentry->args && m->tabentry->args[0]);
- if ((m->child_pid = child_process_create(m->tabentry->args[0], m->tabentry->args, &ifd, &ofd, child_exit_cb, m)) < 0)
+ if ((m->child_pid = child_process_create(m->tabentry->args[0], m->tabentry->args, &ifd, &ofd, child_exit_cb, m)) < 0) {
+ dtmf_fifo_free(m->dtmf_fifo);
return -1;
+ }
assert(ifd >= 0 && ofd >= 0 && !m->child_buffio);
m->child_buffio = buffio_new(ifd, ofd);
- buffio_set_input_watermark(m->child_buffio, INPUT_WATERMARK_CHILD);
+ buffio_set_input_range(m->child_buffio, INPUT_RANGE_CHILD);
+ buffio_set_output_range(m->child_buffio, OUTPUT_RANGE_CHILD);
m->child_buffio->user = m;
m->child_buffio->eof_cb = child_eof_cb;
m->child_buffio->epipe_cb = child_epipe_cb;
m->child_buffio->input_ready_cb = child_input_ready_cb;
- m->child_buffio->output_empty_cb = child_output_empty_cb;
+ m->child_buffio->output_request_cb = child_output_request_cb;
m->child_buffio->error_cb = error_cb;
return 0;
@@ -439,6 +491,10 @@ static void* oop_timeout_cb(oop_source *source, struct timeval tv, void *user) {
daemon_log(LOG_ERR, "Timeout reached for device <%s>", m->dev);
return OOP_HALT;
+ case MODEM_STATE_RINGING:
+ modem_accept(m);
+ return OOP_CONTINUE;
+
case MODEM_STATE_ATA:
case MODEM_STATE_VTXVRX:
@@ -555,6 +611,7 @@ static int modem_input_ready_cb(struct buffio *b, void *user) {
struct tabentry *t;
c[strcspn(c, "\n\r")] = 0;
+
free(m->ring_number);
m->ring_number = strdup(c);
assert(m->ring_number);
@@ -563,27 +620,46 @@ static int modem_input_ready_cb(struct buffio *b, void *user) {
daemon_log(LOG_INFO, "Incoming call from [%s] to [%s]", m->caller_number, m->ring_number);
if ((t = msntab_check_call(m->ring_number, m->caller_number)) && (t->action == CALL_ACTION_ACCEPT || t->action == CALL_ACTION_HANGUP)) {
+
if (m->tabentry)
msntab_unref(m->tabentry);
m->tabentry = t;
- daemon_log(LOG_INFO, "Accepting call.");
-
- m->state = MODEM_STATE_ATA;
- modem_timeout(m, ACCEPT_TIMEOUT);
- buffio_command(m->buffio, "ATA\n");
- } else {
- if (t)
- msntab_unref(t);
- daemon_log(LOG_INFO, "Ignoring call.");
- m->state = MODEM_STATE_CALLER_NUMBER_EXPECT;
- modem_input_ready_cb(b, user);
+ if (t->rings <= 0)
+ modem_accept(m);
+ else {
+ daemon_log(LOG_INFO, "Will accept call after %u rings (%u seconds).", t->rings, t->rings*RING_TIME);
+ m->state = MODEM_STATE_RINGING;
+ modem_timeout(m, RING_TIME*t->rings);
+ }
+
+ break;
}
+
+ daemon_log(LOG_INFO, "Ignoring call.");
+
+ if (t)
+ msntab_unref(t);
+
+ m->state = MODEM_STATE_CALLER_NUMBER_EXPECT;
+ modem_input_ready_cb(b, user);
}
break;
}
+ case MODEM_STATE_RINGING:
+
+ if (buffio_find_input(m->buffio, "RUNG\r\n")) {
+ daemon_log(LOG_INFO, "Peer hung up prematurely.");
+ m->state = MODEM_STATE_CALLER_NUMBER_EXPECT;
+ modem_timeout(m, 0);
+ modem_input_ready_cb(b, user);
+ } else
+ buffio_dump_lines(m->buffio);
+
+ break;
+
case MODEM_STATE_ATA:
if (buffio_find_input(m->buffio, "VCON\r\n")) {
@@ -621,8 +697,13 @@ static int modem_input_ready_cb(struct buffio *b, void *user) {
modem_hangup(m, 1);
} else {
m->dle_flag = 0;
- buffio_set_input_watermark(m->buffio, INPUT_WATERMARK_CONNECTION);
+ buffio_set_input_range(m->buffio, INPUT_RANGE_CONNECTION);
+ buffio_set_output_range(m->buffio, OUTPUT_RANGE_CONNECTION);
buffio_set_delay_usec(m->buffio, ISDN_DELAY_USEC, ISDN_LATENCY_USEC);
+ buffio_set_prebuf(m->buffio, 1);
+
+ m->flush_msg = 0;
+
modem_input_ready_cb(b, user);
}
@@ -650,10 +731,6 @@ static int modem_output_empty_cb(struct buffio *b, void *user) {
switch (m->state) {
- case MODEM_STATE_CONNECTION:
- copy_child_to_modem(m);
- break;
-
case MODEM_STATE_PRE_HANGUP_HUPSEQ:
if (b)
buffio_command(m->buffio, hup_sequence);
@@ -663,8 +740,6 @@ static int modem_output_empty_cb(struct buffio *b, void *user) {
buffio_command(m->buffio, ath_sequence);
m->state = MODEM_STATE_HANGUP;
- daemon_log(LOG_INFO, "Delayed HANGUP sequence");
-
/* Don't restart timeout here */
break;
@@ -680,6 +755,25 @@ static int modem_output_empty_cb(struct buffio *b, void *user) {
return 0;
}
+static int modem_output_request_cb(struct buffio *b, void *user) {
+ struct modem *m = user;
+ assert(b && m && m->buffio == b);
+
+ switch (m->state) {
+
+ case MODEM_STATE_CONNECTION:
+ //daemon_log(LOG_INFO, "modem_output_request");
+ copy_child_to_modem(m);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+
static int modem_dle_cb(uint8_t c, void *user) {
struct modem *m = user;
assert(m);
@@ -696,10 +790,10 @@ static int modem_dle_cb(uint8_t c, void *user) {
return -1;
default:
- daemon_log(LOG_INFO, "Recieved DTMF character '%c'", c);
+ assert(m->dtmf_fifo);
+ dtmf_fifo_pass(m->dtmf_fifo, c);
+ return 0;
}
-
- return 0;
}
static void copy_modem_to_child(struct modem *m) {
@@ -708,10 +802,27 @@ static void copy_modem_to_child(struct modem *m) {
size_t sl, dl;
assert(m->buffio && m->child_buffio);
- sp = buffio_read_ptr(m->buffio, &sl);
- dp = buffio_write_ptr(m->child_buffio, &dl);
+
+ for (;;) {
+ sp = buffio_read_ptr(m->buffio, &sl);
+ dp = buffio_write_ptr(m->child_buffio, &dl);
+
+ if (sp && sl && (!dp || !dl)) {
+ if (!m->flush_msg) {
+ daemon_log(LOG_INFO, "Child too slow, output buffer overflow, flushing.");
+ m->flush_msg = 1;
+ }
+
+ buffio_flush_output(m->child_buffio);
+ continue;
+ }
+
+ break;
+ }
if (sp && sl && dp && dl) {
+ //daemon_log(LOG_INFO, "copy modem->child (%lu->%lu)", (unsigned long) m->buffio->input_length, (unsigned long) (m->child_buffio->output_range - m->child_buffio->output_length));
+
sl = dle_decode(sp, sl, dp, &dl, modem_dle_cb, m, &m->dle_flag);
/* It may be the case that dle_decode recieved a
@@ -728,11 +839,15 @@ static void copy_child_to_modem(struct modem *m) {
const uint8_t *sp;
uint8_t *dp;
- assert(m->child_buffio && m->buffio);
+ assert(m && m->child_buffio && m->buffio);
+
sp = buffio_read_ptr(m->child_buffio, &sl);
dp = buffio_write_ptr(m->buffio, &dl);
-
+
if (sp && dp && sl && dl) {
+
+ //daemon_log(LOG_INFO, "copy child->modem (%lu->%lu)", (unsigned long) m->child_buffio->input_length, (unsigned long) (m->buffio->output_range - m->buffio->output_length));
+
sl = dle_encode(sp, sl, dp, &dl);
buffio_read_ptr_inc(m->child_buffio, sl);
buffio_write_ptr_inc(m->buffio, dl);
@@ -750,7 +865,7 @@ static int modem_reopen(struct modem *m) {
buffio_close_input_fd(m->buffio);
}
- daemon_log(LOG_INFO, "Trying to open TTY device '%s' ...", m->dev);
+ daemon_log(LOG_INFO, "Trying to open modem on TTY device '%s' ...", m->dev);
if ((fd = open(m->dev, O_RDWR|O_NDELAY)) < 0) {
daemon_log(LOG_ERR, "Failed to open device '%s': %s", m->dev, strerror(errno));
@@ -786,11 +901,17 @@ static int modem_reopen(struct modem *m) {
m->buffio = buffio_new(fd, fd);
assert(m->buffio);
+ m->buffio->output_request_cb = modem_output_request_cb;
m->buffio->output_empty_cb = modem_output_empty_cb;
m->buffio->input_ready_cb = modem_input_ready_cb;
m->buffio->error_cb = error_cb;
-
+
m->buffio->user = m;
+
+ buffio_set_input_range(m->buffio, INPUT_RANGE_DEFAULT);
+ buffio_set_output_range(m->buffio, OUTPUT_RANGE_DEFAULT);
+ buffio_set_delay_usec(m->buffio, 0, 0);
+
} else
buffio_set_fds(m->buffio, fd, fd);
diff --git a/src/modem.h b/src/modem.h
index 320467a..8fed726 100644
--- a/src/modem.h
+++ b/src/modem.h
@@ -10,6 +10,7 @@ enum modem_state {
MODEM_STATE_CALLER_NUMBER,
MODEM_STATE_RING_EXPECT,
MODEM_STATE_RING,
+ MODEM_STATE_RINGING,
MODEM_STATE_ATA,
MODEM_STATE_VTXVRX,
MODEM_STATE_CONNECTION,
@@ -36,9 +37,11 @@ struct modem {
struct buffio *child_buffio;
pid_t child_pid;
+ struct dtmf_fifo *dtmf_fifo;
+
struct timeval timeout;
- char *local_msn;
+ char *listen_msn;
char *ring_number;
char *caller_number;
@@ -46,6 +49,7 @@ struct modem {
struct tabentry *tabentry;
int dle_flag;
+ int flush_msg;
};
struct modem *modem_open(const char *dev);
diff --git a/src/msntab.c b/src/msntab.c
index bcd3030..09709cc 100644
--- a/src/msntab.c
+++ b/src/msntab.c
@@ -1,12 +1,39 @@
#include <stddef.h>
#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fnmatch.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <libdaemon/dlog.h>
+
#include "msntab.h"
+#define MAX_ENTRIES 100
+#define MAX_INCLUDES 10
+
+static struct tabentry *first = NULL;
+static struct tabentry *last = NULL;
+static int n_entries = 0;
+
+static int n_includes = 0;
+
struct tabentry* msntab_check_call(const char *callee, const char *caller) {
- static char *args[] = { "/bin/cat", NULL };
- static struct tabentry ca = { CALL_ACTION_ACCEPT, 1, args };
+ struct tabentry *l = first;
- return msntab_ref(&ca);
+ while (l) {
+ assert(l->local && l->remote);
+
+ if (!fnmatch(l->local, callee, 0) && !fnmatch(l->remote, caller, 0)) {
+ daemon_log(LOG_INFO, "MSN table entry from '%s:%u' matched.", l->filename, l->line);
+ return msntab_ref(l);
+ }
+
+ l = l->next;
+ }
+
+ return NULL;
}
@@ -19,5 +46,275 @@ struct tabentry* msntab_ref(struct tabentry *t) {
void msntab_unref(struct tabentry *t) {
assert(t && t->ref_counter >= 1);
t->ref_counter--;
+
+ if (t->ref_counter == 0) {
+
+ if (t->args) {
+ char **a = t->args;
+ while (*a) {
+ free(*a);
+ a++;
+ }
+ free(t->args);
+ }
+
+ free(t->local);
+ free(t->remote);
+ free(t->filename);
+
+ free(t);
+ }
+}
+
+void msntab_flush(void) {
+ while (first) {
+ struct tabentry *l = first;
+ first = first->next;
+
+ if (first)
+ first->prev = NULL;
+
+ if (last == l)
+ last = NULL;
+
+ msntab_unref(l);
+ }
+
+ n_entries = 0;
+ n_includes++;
+}
+
+#define MAX_ARGS 16
+
+static char** parse_args(const char *s) {
+ char *o, **a;
+ char *c = (char*) s;
+ int i = 0;
+
+ a = malloc(sizeof(char *)*MAX_ARGS);
+ memset(a, 0, sizeof(char *)*MAX_ARGS);
+
+ while ((o = strsep(&c, " \t"))) {
+ a[i++] = strdup(o);
+
+ if (i >= MAX_ARGS-1)
+ break;
+ }
+
+ return a;
+}
+
+
+static int parse_options(const char *s, struct tabentry *t) {
+ char *o;
+ char *c = (char*) s;
+
+ assert(s && t);
+
+ while ((o = strsep(&c, ","))) {
+
+ if (!strcmp(o, "defaults"))
+ continue;
+ else if (!strcmp(o, "shbuf")) {
+ t->shbuf = 1;
+ continue;
+ } else if (!strncmp(o, "rings=", 6)) {
+ t->rings = atoi(o+6);
+ continue;
+ }
+
+ daemon_log(LOG_INFO, "Unknown option '%s'", o);
+ return -1;
+ }
+
+ return 0;
}
+
+int msntab_load(const char *fn) {
+ int n;
+ struct tabentry *t = NULL;
+ FILE *f = NULL;
+
+ daemon_log(LOG_INFO, "Loading MSN table '%s'.", fn);
+ if (!(f = fopen(fn, "r"))) {
+ daemon_log(LOG_ERR, "Failed to open MSN table '%s'.", fn);
+ goto fail;
+ }
+
+ n = 0;
+ while (!feof(f)) {
+ char l[256], *c, *e, *local, *remote, *options, *action;
+
+ n++;
+
+ if (!fgets(l, sizeof(l), f))
+ break;
+
+ c = l+strspn(l, " \t");
+ if ((e = strchr(c, '\r')))
+ *e = 0;
+ if ((e = strchr(c, '\n')))
+ *e = 0;
+ e = strchr(c, 0);
+
+ if (*c == '#' || *c == 0)
+ continue;
+
+ if (!(local = strsep(&c, " \t"))) {
+ daemon_log(LOG_ERR, "Parse failure on local MSN field in '%s:%i'.", fn, n);
+ goto fail;
+ }
+
+ if (c)
+ c+=strspn(c, " \t");
+
+ if (!strcmp(local, "@include")) {
+ char *include;
+
+ if (n_includes ++ >= MAX_INCLUDES) {
+ daemon_log(LOG_ERR, "Recursive include directive detected.");
+ goto fail;
+ }
+
+ if (!(include = strsep(&c, ""))) {
+ daemon_log(LOG_ERR, "Parse failure on include field in '%s:%i'.", fn, n);
+ goto fail;
+ }
+
+ if (msntab_load(include) < 0)
+ goto fail;
+
+ continue;
+ }
+
+ if (!(remote = strsep(&c, " \t"))) {
+ daemon_log(LOG_ERR, "Parse failure on remote MSN field in '%s:%i'.", fn, n);
+ goto fail;
+ }
+
+ if (c)
+ c+=strspn(c, " \t");
+
+ if (!(options = strsep(&c, " \t"))) {
+ daemon_log(LOG_ERR, "Parse failure on options field in '%s:%i'.", fn, n);
+ goto fail;
+ }
+
+ if (c)
+ c+=strspn(c, " \t");
+
+ if (!(action = strsep(&c, ""))) {
+ daemon_log(LOG_ERR, "Parse failure on action field in '%s:%i'.", fn, n);
+ goto fail;
+ }
+
+ t = malloc(sizeof(struct tabentry));
+ assert(t);
+ memset(t, 0, sizeof(struct tabentry));
+
+ t->line = n;
+ t->filename = strdup(fn);
+
+ t->ref_counter = 1;
+
+ t->local = strdup(local);
+ assert(t->local);
+
+ t->remote = strdup(remote);
+ assert(t->remote);
+
+ if (action[0] == '@') {
+ if (!strcmp(action, "@hangup"))
+ t->action = CALL_ACTION_HANGUP;
+ else if (!strcmp(action, "@ignore"))
+ t->action = CALL_ACTION_IGNORE;
+ else {
+ daemon_log(LOG_ERR, "Unknown action command '%s' in '%s:%i'.", action, fn, n);
+ goto fail;
+ }
+ } else {
+ t->action = CALL_ACTION_ACCEPT;
+ t->args = parse_args(action);
+ }
+
+ if (parse_options(options, t) < 0) {
+ daemon_log(LOG_ERR, "Parse failure on options field in '%s:%i'.", fn, n);
+ goto fail;
+ }
+
+ if (last) {
+ t->prev = last;
+ last->next = t;
+ last = t;
+ } else
+ last = first = t;
+
+ n_entries ++;
+
+ t = NULL;
+
+ if (n_entries > MAX_ENTRIES) {
+ daemon_log(LOG_INFO, "Too many MSN table entries");
+ goto fail;
+ }
+ }
+
+ fclose(f);
+ daemon_log(LOG_INFO, "MSN table '%s' successfully read.", fn);
+
+ return 0;
+
+fail:
+
+ if (t)
+ msntab_unref(t);
+
+ if (f)
+ fclose(f);
+
+ return -1;
+}
+
+
+static void dump_entry(struct tabentry *t) {
+ char s[256];
+ assert(t);
+
+ s[0] = 0;
+
+ if (t->args) {
+ char **a = t->args;
+
+ while (*a) {
+ char *p = strchr(s, 0);
+ snprintf(p, sizeof(s)-(p-s), a == t->args ? "%s" : " %s", *a);
+ a++;
+ }
+ } else
+ strncpy(s, "NOARGS", sizeof(s));
+
+ daemon_log(LOG_INFO, "[%s:%02u] %-12s -> %-12s; shbuf=%-3s; rings=%u; action=%s; args=<%s>",
+ t->filename,
+ t->line,
+ t->local,
+ t->remote,
+ t->shbuf ? "yes" : "no",
+ t->rings,
+ t->action == CALL_ACTION_ACCEPT ? "ACCEPT" : (t->action == CALL_ACTION_HANGUP ? "HANGUP" : "IGNORE"),
+ s);
+}
+
+void msntab_dump(void) {
+ struct tabentry *l;
+
+ daemon_log(LOG_INFO, "=== Dumping MSN table ===");
+
+ l = first;
+ while (l) {
+ dump_entry(l);
+ l = l->next;
+ }
+
+ daemon_log(LOG_INFO, "=== MSN table dump complete ===");
+}
diff --git a/src/msntab.h b/src/msntab.h
index 28de484..9c473e3 100644
--- a/src/msntab.h
+++ b/src/msntab.h
@@ -5,13 +5,28 @@ enum call_action { CALL_ACTION_IGNORE, CALL_ACTION_ACCEPT, CALL_ACTION_HANGUP };
struct tabentry {
enum call_action action;
- int ref_counter;
+ unsigned ref_counter;
char **args;
+ char *local;
+ char *remote;
+
+ struct tabentry *next;
+ struct tabentry *prev;
+
+ int shbuf;
+ unsigned rings;
+
+ char *filename; /* filename of the msntab where this entry originates from */
+ unsigned line;
};
struct tabentry* msntab_check_call(const char *callee, const char *caller);
struct tabentry* msntab_ref(struct tabentry *t);
void msntab_unref(struct tabentry *t);
+void msntab_flush(void);
+int msntab_load(const char *fn);
+void msntab_dump(void);
+
#endif