From 263b632288c0662ae4ef08925291f8d1a55c1311 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 2 Jan 2004 22:09:35 +0000 Subject: this stuff works great git-svn-id: file:///home/lennart/svn/public/ivam2/trunk@10 dbf6933d-3bce-0310-9bcc-ed052ba35b35 --- src/Makefile | 2 +- src/buffio.c | 436 ++++++++++++++++++++++++++++++++++++------- src/buffio.h | 52 +++++- src/dle.c | 16 +- src/dle.h | 4 +- src/exec.c | 24 ++- src/main.c | 1 + src/modem.c | 575 ++++++++++++++++++++++++++++++++++++++++++++++----------- src/modem.h | 13 +- src/modemman.c | 2 +- src/util.c | 12 +- 11 files changed, 926 insertions(+), 211 deletions(-) diff --git a/src/Makefile b/src/Makefile index 5aff207..65ee382 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 +ivamd: modem.o main.o modemman.o buffio.o exec.o dle.o lock.o util.o msntab.o timevalarith.o $(CC) $(LIBS) -o $@ $^ clean: diff --git a/src/buffio.c b/src/buffio.c index 25ba5a4..727821d 100644 --- a/src/buffio.c +++ b/src/buffio.c @@ -3,16 +3,21 @@ #include #include #include +#include #include #include +#include #include "buffio.h" #include "main.h" +#include "timevalarith.h" -#define BUFSIZE 10240 +//#define IODEBUG 1 +#define BUFSIZE PIPE_BUF +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); @@ -29,6 +34,9 @@ struct buffio* buffio_new(int ifd, int ofd) { b->ifd = ifd; b->ofd = ofd; + + daemon_nonblock(b->ifd, 1); + daemon_nonblock(b->ofd, 1); b->input_buf = malloc(b->input_max_length = BUFSIZE); assert(b->input_buf); @@ -46,36 +54,99 @@ struct buffio* buffio_new(int ifd, int ofd) { return b; } -static void buffio_close_fd(struct buffio *b) { +void buffio_close_input_fd(struct buffio *b) { assert(b); - if (b->b_read_cb) + if (b->b_read_cb) { event_source->cancel_fd(event_source, b->ifd, OOP_READ); + b->b_read_cb = 0; + } - if (b->b_write_cb) + if (b->ifd >= 0) { + + if (b->ofd != b->ifd) + close(b->ifd); + + b->ifd = -1; + } +} + +void buffio_close_output_fd(struct buffio *b) { + assert(b); + + if (b->b_write_cb) { event_source->cancel_fd(event_source, b->ofd, OOP_WRITE); + b->b_write_cb = 0; + } - if (b->ifd >= 0) - close(b->ifd); - if (b->ofd >= 0 && b->ofd != b->ifd) - close(b->ofd); + if (b->ofd >= 0) { - b->b_read_cb = b->b_write_cb = 0; - b->ifd = b->ofd = -1; + if (b->ofd != b->ifd) + close(b->ofd); + + b->ofd = -1; + } } - void buffio_free(struct buffio *b) { - buffio_close_fd(b); + buffio_close_input_fd(b); + buffio_close_output_fd(b); + + if (b->delaying) + event_source->cancel_time(event_source, b->timeout_tv, buffio_timeout_cb, b); + free(b->output_buf); free(b->input_buf); free(b); } +static void* buffio_user_cb(oop_source *source, struct timeval tv, void *user) { + struct buffio *b = user; + enum buffio_sched_cb s; + + int r = 0; + assert(b && source && source == event_source); + + s = b->sched_cb; + b->sched_cb = BUFFIO_SCHED_CB_IDLE; + + if (s & BUFFIO_SCHED_CB_INPUT_READY && b->input_ready_cb && b->input_length) + 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_EOF && b->eof_cb) + r |= b->eof_cb(b, b->user); + if (s & BUFFIO_SCHED_CB_EPIPE && b->epipe_cb) + r |= b->epipe_cb(b, b->user); + if (s & BUFFIO_SCHED_CB_ERROR && b->error_cb) + r |= b->error_cb(b, b->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_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; + if (s & BUFFIO_SCHED_CB_ERROR && !b->error_cb) s = ~BUFFIO_SCHED_CB_ERROR; + + if (s == BUFFIO_SCHED_CB_IDLE) + return; + + if (!b->sched_cb) { + assert(event_source); + event_source->on_time(event_source, OOP_TIME_NOW, buffio_user_cb, b); + } + + b->sched_cb |= s; +} + #ifdef IODEBUG -static void esc_print(const char*c, const uint8_t *b, size_t l) { +static void esc_print(struct buffio *bio, const char*c, const uint8_t *b, size_t l) { size_t i; - fprintf(stderr, "%s", c); + fprintf(stderr, "[%p] %s", bio, c); for (i = 0; i < l; i++, b++) fputc(*b < 32 ? '.' : *b, stderr); @@ -83,7 +154,7 @@ static void esc_print(const char*c, const uint8_t *b, size_t l) { }; #endif -static void buffio_normalize(struct buffio *b) { +inline static void buffio_normalize(struct buffio *b) { assert(b); assert(b->input_index < b->input_max_length); @@ -92,14 +163,20 @@ static void buffio_normalize(struct buffio *b) { if (!b->input_length) /* This will optimize throughput a bit */ b->input_index = 0; + assert(b->output_index < b->output_max_length); + assert(b->output_length <= b->output_max_length); + if (!b->output_length) /* This will optimize throughput a bit */ b->output_index = 0; } static void buffio_set_input_callbacks(struct buffio *b) { - assert(b && b->ifd >= 0 && event_source); + assert(b && event_source); - if (b->input_length <= b->input_watermark) { + if (b->ifd == -1) + return; + + if (!b->readable && b->input_length < b->input_watermark) { 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; @@ -113,9 +190,12 @@ static void buffio_set_input_callbacks(struct buffio *b) { } static void buffio_set_output_callbacks(struct buffio *b) { - assert(b && b->ofd >= 0 && event_source); + assert(b && event_source); - if (b->output_length >= b->output_watermark) { + if (b->ofd == -1) + return; + + if (!b->writable && b->output_length >= b->output_watermark) { 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; @@ -128,11 +208,44 @@ static void buffio_set_output_callbacks(struct buffio *b) { } } -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); +static void buffio_delay(struct buffio *b, unsigned bytes) { + assert(event_source && event_source->on_time); + + if (b->delaying) { + event_source->cancel_time(event_source, b->timeout_tv, buffio_timeout_cb, b); + b->delaying = 0; + } + + if (bytes && b->output_delay_usec) { + struct timeval now; + + gettimeofday(&now, NULL); + + /* Time when the last write operation is complete */ + b->finish_tv = timeval_add(timeval_max(b->finish_tv, now), (uint64_t) bytes * b->output_delay_usec); + + /* Time when we want to write the next data to the device */ + b->timeout_tv = timeval_sub(b->finish_tv, b->output_latency_usec); + + assert(event_source && event_source->on_time); + event_source->on_time(event_source, b->timeout_tv, buffio_timeout_cb, b); + b->delaying = 1; + +// daemon_log(LOG_INFO, "%u bytes, now = %lu|%lu; finish = %lu|%lu; timeout = %lu|%lu", bytes, now.tv_sec, now.tv_usec, b->finish_tv.tv_sec, b->finish_tv.tv_usec, b->timeout_tv.tv_sec, b->timeout_tv.tv_usec); + } +} - if (b->input_length < b->input_max_length) { +void buffio_set_delay_usec(struct buffio *b, unsigned long delay, unsigned long latency) { + assert(b); + + b->output_delay_usec = delay; + b->output_latency_usec = latency; +} + +static void do_read(struct buffio *b) { + assert(b); + + if (b->readable && b->input_length < b->input_watermark) { ssize_t s; size_t m, i; @@ -141,41 +254,47 @@ static void* buffio_read_cb(oop_source *source, int fd, oop_event event, void *u m = b->input_max_length-b->input_length; if (m > b->input_max_length-i) m = b->input_max_length-i; - - if ((s = read(fd, b->input_buf+i, m)) < 0) { + + 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)); - return OOP_HALT; - } + 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 { #ifdef IODEBUG - esc_print("INPUT: ", b->input_buf+i, s); + esc_print(b, "INPUT: ", b->input_buf+i, s); #endif - - if (!s) { - buffio_close_fd(b); - if (b->eof_cb) - b->eof_cb(b, b->user); - return OOP_CONTINUE; + + assert(s <= m); + b->input_length += s; + buffio_normalize(b); } - - assert(s <= m); - b->input_length += s; } - buffio_normalize(b); buffio_set_input_callbacks(b); - if (b->input_length && b->input_ready_cb) - b->input_ready_cb(b, b->user); + if (b->input_length) + buffio_sched_cb(b, BUFFIO_SCHED_CB_INPUT_READY); - return OOP_CONTINUE; + return; } -static void* buffio_write_cb(oop_source *source, int fd, oop_event event, void *user) { - struct buffio *b = user; - assert(source && b && b->ofd == fd && event == OOP_WRITE); +static void do_write(struct buffio *b) { + assert(b); - if (b->output_length > 0) { + if (!b->delaying && b->writable && b->output_length >= b->output_watermark) { ssize_t s; size_t m; @@ -183,37 +302,73 @@ static void* buffio_write_cb(oop_source *source, int fd, oop_event event, void * if (m > b->output_max_length-b->output_index) m = b->output_max_length-b->output_index; - if ((s = write(fd, b->output_buf+b->output_index, m)) < 0) { - daemon_log(LOG_ERR, "Failed to write to file descriptor: %s", strerror(errno)); - return OOP_HALT; - } + s = write(b->ofd, b->output_buf+b->output_index, m); + b->writable = 0; -#ifdef IODEBUG - esc_print("OUTPUT: ", b->output_buf+b->output_index, s); -#endif + //daemon_log(LOG_INFO, "%p: Wrote %u (%u) bytes.", b, s, m); - if (!s) { - buffio_close_fd(b); - if (b->eof_cb) - b->eof_cb(b, b->user); - return OOP_CONTINUE; + if (s < 0) { + buffio_close_output_fd(b); + + 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; + } } + +#ifdef IODEBUG + esc_print(b, "OUTPUT: ", b->output_buf+b->output_index, s); +#endif - assert(s <= m); + 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_output_callbacks(b); + buffio_normalize(b); + buffio_delay(b, s); - if (!b->output_length && b->output_empty_cb) - b->output_empty_cb(b, b->user); + if (!b->output_length) + buffio_sched_cb(b, BUFFIO_SCHED_CB_OUTPUT_EMPTY); + } else + buffio_set_output_callbacks(b); + + return; +} + +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; + do_read(b); return OOP_CONTINUE; +} +static void* buffio_write_cb(oop_source *source, int fd, oop_event event, void *user) { + struct buffio *b = user; + assert(source && b && b->ofd == fd && event == OOP_WRITE); + + b->writable = 1; + do_write(b); + + return OOP_CONTINUE; } +static void* buffio_timeout_cb(oop_source *source, struct timeval tv, void *user) { + struct buffio *b = user; + assert(source && b && b->delaying); + + b->delaying = 0; + do_write(b); + + return OOP_CONTINUE; +} int buffio_write(struct buffio *b, const uint8_t *d, size_t l) { assert(b && d && l); @@ -229,9 +384,6 @@ int buffio_write(struct buffio *b, const uint8_t *d, size_t l) { i = (b->output_index + b->output_length) % b->output_max_length; m = l; - if (m > b->output_max_length-b->output_length) - m = b->output_max_length-b->output_length; - if (m > b->output_max_length-i) m = b->output_max_length-i; @@ -241,9 +393,9 @@ int buffio_write(struct buffio *b, const uint8_t *d, size_t l) { b->output_length += m; } - buffio_set_output_callbacks(b); buffio_normalize(b); - + + do_write(b); return 0; } @@ -266,6 +418,13 @@ void buffio_flush_input(struct buffio *b) { buffio_set_input_callbacks(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; @@ -284,7 +443,8 @@ int buffio_find_input(struct buffio *b, const char *c) { b->input_length = l-cl; buffio_normalize(b); - buffio_set_input_callbacks(b); + + do_read(b); return 1; } } @@ -293,7 +453,7 @@ int buffio_find_input(struct buffio *b, const char *c) { } -char *buffio_read_line(struct buffio *b, char *c, size_t l) { +char* buffio_read_line(struct buffio *b, char *c, size_t l) { size_t i; assert(b && c && l); @@ -312,8 +472,10 @@ 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_set_input_callbacks(b); + buffio_normalize(b); + + do_read(b); return c; } } @@ -341,3 +503,135 @@ void buffio_dump(struct buffio *b) { } fprintf(stderr, "]\n"); } + +void buffio_set_input_watermark(struct buffio *b, ssize_t w) { + assert(b); + + if (w < 0) + b->input_watermark = b->input_max_length; + else + b->input_watermark = w; + + assert(b->input_watermark > 0 && b->input_watermark <= b->input_max_length); + buffio_set_input_callbacks(b); +} + +void buffio_set_output_watermark(struct buffio *b, ssize_t w) { + assert(b); + + if (w < 0) + b->output_watermark = b->output_max_length; + else + b->output_watermark = w; + + assert(b->output_watermark > 0 && b->output_watermark <= b->output_max_length); + buffio_set_output_callbacks(b); +} + + +const uint8_t* buffio_read_ptr(struct buffio *b, size_t *l) { + assert(b && l); + + *l = b->input_length; + + if (*l > b->input_max_length - b->input_index) + *l = b->input_max_length - b->input_index; + + return b->input_buf + b->input_index; +} + +void buffio_read_ptr_inc(struct buffio *b, size_t l) { + assert(b); + + if (!l) + return; + + assert(l <= b->input_length && l <= b->input_max_length - b->input_index); + b->input_index += l; + + while (b->input_index >= b->input_max_length) + b->input_index -= b->input_max_length; + + b->input_length -= l; + + buffio_normalize(b); + + do_read(b); +} + +uint8_t* buffio_write_ptr(struct buffio *b, size_t *l) { + size_t j; + assert(b && l); + + j = (b->output_index + b->output_length) % b->output_max_length; + + *l = b->output_max_length - b->output_length; + if (*l > b->output_max_length - j) + *l = b->output_max_length - j; + + return b->output_buf + j; +} + +void buffio_write_ptr_inc(struct buffio *b, size_t l) { + int j; + assert(b); + + if (!l) + return; + + j = (b->output_index + b->output_length) % b->output_max_length; + 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); +} + +int buffio_output_is_empty(struct buffio *b) { + assert(b); + + return b->output_length == 0; +} + +void buffio_dump_lines(struct buffio *b) { + int r, i; + assert(b); + + r = 0; + + for (i = 0; i < b->input_length; i++) + if (b->input_buf[(b->input_index+i) % b->input_max_length] == '\n') + r = i+1; + + if (r > 0) { + b->input_index = (b->input_index+r) % b->input_max_length; + b->input_length -= r; + + buffio_normalize(b); + + do_read(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, 1); + daemon_nonblock(b->ofd, 1); + + b->readable = b->writable = 0; + + buffio_set_input_callbacks(b); + buffio_set_output_callbacks(b); +} + +int buffio_can_write(struct buffio *b, size_t l) { + assert(b); + + return l <= b->output_max_length - b->output_length; +} diff --git a/src/buffio.h b/src/buffio.h index e0a2d0a..a41ec76 100644 --- a/src/buffio.h +++ b/src/buffio.h @@ -4,6 +4,15 @@ #include #include +enum buffio_sched_cb { + BUFFIO_SCHED_CB_IDLE = 0, + BUFFIO_SCHED_CB_INPUT_READY = 1, + BUFFIO_SCHED_CB_OUTPUT_EMPTY = 2, + BUFFIO_SCHED_CB_EOF = 4, + BUFFIO_SCHED_CB_EPIPE = 8, + BUFFIO_SCHED_CB_ERROR = 16 +}; + struct buffio { int ifd; int ofd; @@ -18,9 +27,21 @@ struct buffio { void *user; - void (*eof_cb) (struct buffio *b, void *user); - void (*output_empty_cb) (struct buffio *b, void *user); - void (*input_ready_cb) (struct buffio *b, void *user); + int readable; + int writable; + + int (*input_ready_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); + int (*error_cb) (struct buffio *b, void *user); + + enum buffio_sched_cb sched_cb; + + uint32_t output_delay_usec; + uint32_t output_latency_usec; + int delaying; + struct timeval finish_tv, timeout_tv; }; struct buffio* buffio_new(int ifd, int ofd); @@ -29,11 +50,34 @@ 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); -int buffio_command(struct buffio *b, const char *c); void buffio_flush_input(struct buffio *b); +void buffio_flush_output(struct buffio *b); + +int 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); + +const uint8_t* buffio_read_ptr(struct buffio *b, size_t *l); +void buffio_read_ptr_inc(struct buffio *b, size_t l); + +uint8_t* buffio_write_ptr(struct buffio *b, size_t *l); +void buffio_write_ptr_inc(struct buffio *b, size_t l); + +void buffio_close_input_fd(struct buffio *b); +void buffio_close_output_fd(struct buffio *b); + +int buffio_output_is_empty(struct buffio *b); +void buffio_dump_lines(struct buffio *b); + +void buffio_set_fds(struct buffio *b, int ifd, int ofd); + +void buffio_set_delay_usec(struct buffio *b, unsigned long usec, unsigned long latency); + +int buffio_can_write(struct buffio *b, size_t l); + #endif diff --git a/src/dle.c b/src/dle.c index 680c82f..1cf9fa7 100644 --- a/src/dle.c +++ b/src/dle.c @@ -2,7 +2,7 @@ #include "dle.h" -size_t dle_decode(uint8_t* s, size_t ls, uint8_t* d, size_t *ld, void (*dle_func) (uint8_t c, void *user), void *user, int *dle_flag) { +size_t dle_decode(const uint8_t* s, size_t ls, uint8_t* d, size_t *ld, int (*dle_func) (uint8_t c, void *user), void *user, int *dle_flag) { size_t ns, nd; assert(s && ls && d && ld && *ld && dle_flag); @@ -12,8 +12,11 @@ size_t dle_decode(uint8_t* s, size_t ls, uint8_t* d, size_t *ld, void (*dle_func if (s[ns] == DLE) d[nd++] = DLE; else { - if (dle_func) - dle_func(s[ns], user); + if (dle_func) + if (dle_func(s[ns], user) < 0) { + ns++; + break; + } } *dle_flag = 0; @@ -29,12 +32,15 @@ size_t dle_decode(uint8_t* s, size_t ls, uint8_t* d, size_t *ld, void (*dle_func return ns; } -size_t dle_encode(uint8_t* s, size_t ls, uint8_t* d, size_t *ld) { +size_t dle_encode(const uint8_t* s, size_t ls, uint8_t* d, size_t *ld) { size_t ns, nd; for (ns = nd = 0; ns < ls && nd < *ld; ns++) { - if (s[ns] == DLE) + if (s[ns] == DLE) { + if (nd+1 >= *ld) + break; d[nd++] = DLE; + } d[nd++] = s[ns]; } diff --git a/src/dle.h b/src/dle.h index 72bfd55..31d511b 100644 --- a/src/dle.h +++ b/src/dle.h @@ -9,7 +9,7 @@ #define ETX 0x03 #define DC4 0x14 -size_t dle_decode(uint8_t* s, size_t ls, uint8_t* d, size_t *ld, void (*dle_func) (uint8_t c, void *user), void *user, int *dle_flag); -size_t dle_encode(uint8_t* s, size_t ls, uint8_t* d, size_t *ld); +size_t dle_decode(const uint8_t* s, size_t ls, uint8_t* d, size_t *ld, int (*dle_func) (uint8_t c, void *user), void *user, int *dle_flag); +size_t dle_encode(const uint8_t* s, size_t ls, uint8_t* d, size_t *ld); #endif diff --git a/src/exec.c b/src/exec.c index 728c574..d9195c6 100644 --- a/src/exec.c +++ b/src/exec.c @@ -15,7 +15,7 @@ struct process_info { pid_t pid; - int killed; + int dead; int stderr_pipe; process_exit_cb_t cb; void *user; @@ -60,7 +60,6 @@ static void close_child_pipe(struct process_info *p) { } - static void free_process_info(struct process_info *p) { assert (p); close_child_pipe(p); @@ -103,22 +102,29 @@ static void *oop_sigchld_cb(oop_source *source, int sig, void *user) { struct process_info *p; assert(source && sig == SIGCHLD); - if ((pid = wait(&status)) <= 0) { + if ((pid = waitpid(-1, &status, WUNTRACED)) <= 0) { daemon_log(LOG_ERR, "wait() failed: %s", strerror(errno)); return OOP_HALT; } + if (WIFSTOPPED(status)) { + daemon_log(LOG_ERR, "Child process stopped!"); + return OOP_CONTINUE; + } + if (!(p = find_process(pid))) { daemon_log(LOG_WARNING, "Got SIGCHLD for unknown process, reaping"); return OOP_CONTINUE; } - assert(p && p->cb); + assert(p && !p->dead); - if (!p->killed) + p->dead = 1; + if (p->cb) p->cb(pid, status, p->user); - remove_process(pid); + if (p->stderr_pipe < 0) + remove_process(pid); return OOP_CONTINUE; } @@ -149,6 +155,10 @@ static void *oop_read_cb(oop_source *source, int fd, oop_event event, void *user if (s == 0) { /* EOF */ close_child_pipe(p); + + if (p->dead) + remove_process(p->pid); + return OOP_CONTINUE; } @@ -224,7 +234,6 @@ pid_t child_process_create(const char *file, char *const argv[], int *ifd, int * p->cb = cb; p->user = user; p->next = procs; - p->killed = 0; p->stderr_pipe = stderr_fds[0]; close(stderr_fds[1]); @@ -324,6 +333,5 @@ int child_process_kill(pid_t pid) { return -1; } - p->killed = 1; return 0; } diff --git a/src/main.c b/src/main.c index f80b605..9710be9 100644 --- a/src/main.c +++ b/src/main.c @@ -35,6 +35,7 @@ int main_loop(void) { event_source->on_signal(event_source, SIGINT, oop_exit_cb, NULL); event_source->on_signal(event_source, SIGTERM, oop_exit_cb, NULL); + signal(SIGPIPE, SIG_IGN); if (oop_sys_run(sys) == OOP_ERROR) { daemon_log(LOG_ERR, "oop_sys_new() returned OOP_ERROR"); diff --git a/src/modem.c b/src/modem.c index 4cf9a49..71a8eb4 100644 --- a/src/modem.c +++ b/src/modem.c @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -24,26 +25,34 @@ /* Baudrate for the TTY. Should be greater the 64000. */ #define BAUD_RATE B115200 -#define RESET_IDLE_TIME 2 +#define RESET_IDLE_TIME 10 #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 INIT_AT_COMMANDS 12 +#define INPUT_WATERMARK_DEFAULT -1 +#define INPUT_WATERMARK_CONNECTION 2 +#define INPUT_WATERMARK_CHILD 1 + +#define INIT_AT_COMMANDS 16 static const char* const init_at_commands[INIT_AT_COMMANDS*2] = { - "\nAT&F\n", + "\nAT&F\n", /* Reset to fabric defaults */ "OK\r\n", - "ATI\n", + "ATI\n", /* Make sure this is an isdn4linux device */ "Linux ISDN\r\nOK\r\n", - "AT&B16\n", + "AT+FCLASS=8\n", /* switch to audio mode */ "OK\r\n", - "AT+FCLASS=8\n", + "AT+VIP\n", /* Reset audio parameters */ "OK\r\n", - - "AT+VSM=6\n", + + "AT+VSM=6\n", /* Set uLaw encoding */ "OK\r\n", "ATS18=1\n", @@ -55,97 +64,75 @@ static const char* const init_at_commands[INIT_AT_COMMANDS*2] = { "ATS13.4=0\n", "OK\r\n", - "ATS13.0=1\n", + "ATS13.0=1\n", /* direct tty send */ + "OK\r\n", + + "AT\n", //S13.2=0\n", /* Don't hangup on DTR low */ "OK\r\n", "ATS23=1\n", "OK\r\n", + + "ATS16=1\n", /* send packet size */ + "OK\r\n", + + "AT\n", //S12.3=0\n", /* DCD always on */ + "OK\r\n", + + "AT\n", //S12.6=0\n", /* DSR always on */ + "OK\r\n", - "ATS0=0\n", + "ATS0=0\n", /* No automatic call answering */ "OK\r\n", - "AT&L%s\n", + "AT&L%s\n", /* Listen on this MSN */ "OK\r\n", }; -static void modem_output_empty_cb(struct buffio *b, void *user); -static void modem_input_ready_cb(struct buffio *b, void *user); +static const char hup_sequence[] = { DLE, DC4, DLE, ETX, 0 }; +static const char ath_sequence[] = "\nATH\n"; + +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); static void modem_init_cycle(struct modem *m); static void modem_timeout(struct modem *m, int t); +static void copy_child_to_modem(struct modem *m); +static void copy_modem_to_child(struct modem *m); +static int modem_reopen(struct modem *m); +void modem_close(struct modem *m); + struct modem *modem_open(const char *dev) { struct modem *m; - int fd = -1, n; - struct termios ts, ts2; assert(dev); if (device_lock(dev) != 0) return NULL; - if ((fd = open(dev, O_RDWR|O_NDELAY)) < 0) { - daemon_log(LOG_ERR, "Failed to open device <%s>: %s", dev, strerror(errno)); - goto fail; - } - - if ((n = fcntl(fd, F_GETFL, 0)) < 0 || fcntl(fd, F_SETFL, n & ~O_NDELAY) < 0) { - daemon_log(LOG_ERR, "Failed to remove O_NDELAY flag from device: %s", strerror(errno)); - goto fail; - } - - memset(&ts, 0, sizeof ts); - ts.c_cflag = CRTSCTS | IGNPAR | HUPCL | CS8; - ts.c_iflag = IGNPAR; - ts.c_oflag = 0; - ts.c_lflag = 0; - - ts.c_cc[VMIN] = 1; - ts.c_cc[VTIME] = 0; - - cfsetospeed(&ts, BAUD_RATE); - cfsetispeed(&ts, BAUD_RATE); - - tcflush(fd, TCIFLUSH); - - if (tcsetattr(fd, TCSANOW, &ts) < 0) { - daemon_log(LOG_ERR, "Failed to set TTY attributes: %s", strerror(errno)); - goto fail; - } - - if (tcgetattr(fd, &ts2) < 0 || memcmp(&ts, &ts2, sizeof(ts)) != 0) { - daemon_log(LOG_ERR, "Failed to set TTY attributes"); - /*goto fail;*/ /* DON'T FORGET TO REMOVE THE COMMENT MARKS */ - } - m = malloc(sizeof(struct modem)); assert(m); memset(m, 0, sizeof(struct modem)); + m->dev = strdup(dev); assert(m->dev); - m->buffio = buffio_new(fd, fd); - assert(m->buffio); - - m->buffio->output_empty_cb = modem_output_empty_cb; - m->buffio->input_ready_cb = modem_input_ready_cb; - m->buffio->user = m; m->child_pid = -1; - + m->child_buffio = NULL; m->local_msn = "46"; m->tabentry = NULL; - - modem_init_cycle(m); + if (modem_reopen(m) < 0) + goto fail; + + modem_init_cycle(m); + return m; fail: - if (fd >= 0) - close(fd); - - device_unlock(dev); - + modem_close(m); return NULL; } @@ -161,14 +148,28 @@ void modem_close(struct modem *m) { if (m->child_pid != -1) child_process_kill(m->child_pid); - buffio_free(m->buffio); + if (m->child_buffio) + buffio_free(m->child_buffio); + + if (m->buffio) + buffio_free(m->buffio); + device_unlock(m->dev); + free(m->dev); free(m->caller_number); free(m->ring_number); free(m); } +static int error_cb(struct buffio *b, void *user) { + struct modem *m = user; + assert(m && (m->buffio == b || m->child_buffio == b)); + + daemon_log(LOG_ERR, "Buffio (%s) failure, exiting...", m->buffio == b ? "modem" : "child"); + return -1; +} + static void modem_timeout(struct modem *m, int t) { assert(m); @@ -192,7 +193,9 @@ static void modem_next_command(struct modem *m) { p = init_at_commands[m->command_index*2]; - if (m->command_index == 11) { + 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); p = tmp; } @@ -210,7 +213,13 @@ static void modem_init_cycle(struct modem *m) { modem_next_command(m); } -void modem_force_hangup(struct modem *m) { +static void modem_sched_reset(struct modem *m) { + daemon_log(LOG_INFO, "Reinitializing in %i seconds.", RESET_IDLE_TIME); + m->state = MODEM_STATE_IDLE; + modem_timeout(m, RESET_IDLE_TIME); +} + +static void modem_force_hangup(struct modem *m) { struct termios pts; assert(m && m->buffio); @@ -225,6 +234,193 @@ void modem_force_hangup(struct modem *m) { tcsetattr(m->buffio->ifd, TCSANOW, &pts); } +static void modem_hangup_seq(struct modem *m) { + assert(m); + + if (m->child_buffio) { + + if (buffio_output_is_empty(m->child_buffio)) + buffio_close_output_fd(m->child_buffio); + + if (m->child_buffio->ifd == -1 && m->child_buffio->ofd == -1) { + buffio_free(m->child_buffio); + m->child_buffio = NULL; + } + } + + if (m->child_buffio && m->child_buffio->ofd != -1) { + /* Draining output is required */ + daemon_log(LOG_INFO, "Waiting for child drain."); + m->state = MODEM_STATE_CHILD_DRAIN; + modem_timeout(m, CHILD_TIMEOUT); + + } else if (m->child_buffio) { + /* Draining input is required */ + daemon_log(LOG_INFO, "Waiting for child EOF."); + m->state = MODEM_STATE_CHILD_WAIT_EOF; + buffio_close_output_fd(m->child_buffio); + buffio_flush_input(m->child_buffio); + modem_timeout(m, CHILD_TIMEOUT); + + } else if (m->child_pid != (pid_t) -1) { + daemon_log(LOG_INFO, "Killing child."); + m->state = MODEM_STATE_CHILD_KILL; + child_process_kill(m->child_pid); + modem_timeout(m, CHILD_TIMEOUT); + + } else + modem_sched_reset(m); +} + +static void modem_hangup(struct modem *m, int b) { + size_t l; + assert(m); + daemon_log(LOG_INFO, "Hanging up."); + + buffio_set_input_watermark(m->buffio, INPUT_WATERMARK_DEFAULT); + buffio_set_delay_usec(m->buffio, 0, 0); + + l = strlen(ath_sequence) + (b ? strlen(hup_sequence) : 0); + + if (buffio_can_write(m->buffio, l)) { + + if (b) + buffio_command(m->buffio, hup_sequence); + + buffio_command(m->buffio, ath_sequence); + m->state = MODEM_STATE_HANGUP; + } else + m->state = b ? MODEM_STATE_PRE_HANGUP_HUPSEQ : MODEM_STATE_PRE_HANGUP_ATHSEQ; + + modem_timeout(m, HANGUP_TIMEOUT); +} + +static void child_exit_cb(pid_t t, int status, void *user) { + struct modem *m = user; + assert(m && m->child_pid == t); + + if (WIFEXITED(status)) + daemon_log(WEXITSTATUS(status) == 0 ? LOG_INFO : LOG_WARNING, "Child exited with return code %i.", WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + daemon_log(LOG_WARNING, "Child exited by signal %i.", WTERMSIG(status)); + else + daemon_log(LOG_ERR, "Child exited due to unknown cause."); + + m->child_pid = (pid_t) -1; + + switch (m->state) { + + case MODEM_STATE_CHILD_KILL: + modem_hangup_seq(m); + break; + + default: + break; + } +} + +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) + copy_child_to_modem(m); + else + buffio_flush_input(m->child_buffio); + + return 0; +} + +static int child_eof_cb(struct buffio *b, void *user) { + struct modem *m = user; + assert(m && m->child_buffio == b); + + daemon_log(LOG_INFO, "Got child EOF."); + + switch (m->state) { + + case MODEM_STATE_CONNECTION: + modem_hangup(m, 1); + break; + + case MODEM_STATE_CHILD_WAIT_EOF: + modem_hangup_seq(m); + break; + + default: + break; + } + + return 0; +} + +static int child_epipe_cb(struct buffio *b, void *user) { + struct modem *m = user; + assert(m && m->child_buffio == b); + + daemon_log(LOG_INFO, "Got child EPIPE."); + + assert(m->child_buffio); + buffio_flush_output(m->child_buffio); + + switch (m->state) { + + case MODEM_STATE_CONNECTION: + modem_hangup(m, 1); + break; + + case MODEM_STATE_CHILD_DRAIN: + modem_hangup_seq(m); + break; + + default: + break; + } + + return 0; +} + +static int child_output_empty_cb(struct buffio *b, void *user) { + struct modem *m = user; + assert(m && m->child_buffio == b); + + switch (m->state) { + + case MODEM_STATE_CONNECTION: + copy_modem_to_child(m); + break; + + case MODEM_STATE_CHILD_DRAIN: + modem_hangup_seq(m); + break; + + default: + break; + } + + return 0; +} + +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->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) + 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); + 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->error_cb = error_cb; + + return 0; +} static void* oop_timeout_cb(oop_source *source, struct timeval tv, void *user) { struct modem *m = (struct modem*) user; @@ -236,14 +432,17 @@ static void* oop_timeout_cb(oop_source *source, struct timeval tv, void *user) { case MODEM_STATE_CALLER_NUMBER: case MODEM_STATE_RING_EXPECT: case MODEM_STATE_RING: + case MODEM_STATE_PRE_HANGUP_HUPSEQ: + case MODEM_STATE_PRE_HANGUP_ATHSEQ: + case MODEM_STATE_HANGUP: daemon_log(LOG_ERR, "Timeout reached for device <%s>", m->dev); return OOP_HALT; - case MODEM_STATE_ANSWER: + case MODEM_STATE_ATA: case MODEM_STATE_VTXVRX: - daemon_log(LOG_ERR, "Connection failed for device <%s>", m->dev); + daemon_log(LOG_ERR, "Connection not established for device <%s>", m->dev); m->state = MODEM_STATE_IDLE; modem_timeout(m, RESET_IDLE_TIME); return OOP_CONTINUE; @@ -252,13 +451,36 @@ static void* oop_timeout_cb(oop_source *source, struct timeval tv, void *user) { modem_init_cycle(m); return OOP_CONTINUE; + + case MODEM_STATE_CHILD_DRAIN: + daemon_log(LOG_INFO, "Child output buffer did not drain, flushing."); + assert(m->child_buffio); + buffio_flush_output(m->child_buffio); + + modem_hangup_seq(m); + return OOP_CONTINUE; + case MODEM_STATE_CHILD_WAIT_EOF: + + daemon_log(LOG_INFO, "Child did not EOF, closing child pipes."); + assert(m->child_buffio); + buffio_free(m->child_buffio); + m->child_buffio = NULL; + + modem_hangup_seq(m); + + return OOP_CONTINUE; + + case MODEM_STATE_CHILD_KILL: + daemon_log(LOG_INFO, "Child did not react on killing, exiting."); + return OOP_HALT; + default: assert(0); } } -static void modem_input_ready_cb(struct buffio *b, void *user) { +static int modem_input_ready_cb(struct buffio *b, void *user) { struct modem *m = user; assert(b && m && m->buffio == b); @@ -280,7 +502,8 @@ static void modem_input_ready_cb(struct buffio *b, void *user) { modem_input_ready_cb(b, user); } else modem_next_command(m); - } + } else + buffio_dump_lines(m->buffio); break; } @@ -291,7 +514,8 @@ static void modem_input_ready_cb(struct buffio *b, void *user) { m->state = MODEM_STATE_CALLER_NUMBER; modem_timeout(m, BASIC_TIMEOUT); modem_input_ready_cb(b, user); - } + } else + buffio_dump_lines(m->buffio); break; @@ -318,7 +542,9 @@ static void modem_input_ready_cb(struct buffio *b, void *user) { if (buffio_find_input(m->buffio, "RING/")) { m->state = MODEM_STATE_RING; modem_input_ready_cb(b, user); - } + } else + buffio_dump_lines(m->buffio); + break; case MODEM_STATE_RING: { @@ -342,7 +568,7 @@ static void modem_input_ready_cb(struct buffio *b, void *user) { m->tabentry = t; daemon_log(LOG_INFO, "Accepting call."); - m->state = MODEM_STATE_ANSWER; + m->state = MODEM_STATE_ATA; modem_timeout(m, ACCEPT_TIMEOUT); buffio_command(m->buffio, "ATA\n"); @@ -358,28 +584,27 @@ static void modem_input_ready_cb(struct buffio *b, void *user) { break; } - case MODEM_STATE_ANSWER: + case MODEM_STATE_ATA: if (buffio_find_input(m->buffio, "VCON\r\n")) { assert(m->tabentry); if (m->tabentry->action == CALL_ACTION_ACCEPT) { daemon_log(LOG_INFO, "Call accepted, changing to voice mode."); - buffio_command(m->buffio, "AT+VTX+VRX"); + buffio_command(m->buffio, "AT+VTX+VRX\n"); m->state = MODEM_STATE_VTXVRX; } else if (m->tabentry->action == CALL_ACTION_HANGUP) { modem_timeout(m, 0); - daemon_log(LOG_INFO, "Call accepted for hang up."); - m->state = MODEM_STATE_ATH; - modem_input_ready_cb(b, user); + modem_hangup(m, 0); } else assert(0); - } else if (buffio_find_input(m->buffio, "NO CARRIER\n\n")) { + + } else if (buffio_find_input(m->buffio, "NO CARRIER\r\n")) { daemon_log(LOG_INFO, "Failed to accept call, carrier lost"); - m->state = MODEM_STATE_IDLE; - modem_timeout(m, RESET_IDLE_TIME); - } + modem_sched_reset(m); + } else + buffio_dump_lines(m->buffio); break; @@ -390,58 +615,190 @@ static void modem_input_ready_cb(struct buffio *b, void *user) { m->state = MODEM_STATE_CONNECTION; daemon_log(LOG_INFO, "Voice connection established."); - modem_input_ready_cb(b, user); - } else if (buffio_find_input(m->buffio, "NO CARRIER\n\n")) { + + if (modem_start_child(m) < 0) { + daemon_log(LOG_ERR, "Failed to start child process"); + modem_hangup(m, 1); + } else { + m->dle_flag = 0; + buffio_set_input_watermark(m->buffio, INPUT_WATERMARK_CONNECTION); + buffio_set_delay_usec(m->buffio, ISDN_DELAY_USEC, ISDN_LATENCY_USEC); + modem_input_ready_cb(b, user); + } + + } else if (buffio_find_input(m->buffio, "NO CARRIER\r\n")) { daemon_log(LOG_INFO, "Failed to accept call, carrier lost"); - m->state = MODEM_STATE_IDLE; - modem_timeout(m, RESET_IDLE_TIME); + modem_sched_reset(m); } break; case MODEM_STATE_CONNECTION: - m->state = MODEM_STATE_HANGUP; - modem_input_ready_cb(b, user); + copy_modem_to_child(m); break; - - case MODEM_STATE_HANGUP: { - static const char hup[] = { DLE, DC4, DLE, ETX, 0 }; - buffio_command(m->buffio, hup); - - /* pass over ... */ - } - - case MODEM_STATE_ATH: { - daemon_log(LOG_INFO, "Hanging up."); - buffio_command(m->buffio, "ATH\n"); - m->state = MODEM_STATE_FORCE_HANGUP; - break; - } default: break; } -} - -static void modem_output_empty_cb(struct buffio *b, void *user) { + return 0; +} + +static int modem_output_empty_cb(struct buffio *b, void *user) { struct modem *m = user; assert(b && m && m->buffio == b); switch (m->state) { - case MODEM_STATE_FORCE_HANGUP: + case MODEM_STATE_CONNECTION: + copy_child_to_modem(m); + break; + + case MODEM_STATE_PRE_HANGUP_HUPSEQ: + if (b) + buffio_command(m->buffio, hup_sequence); + /* pass over */ + + case MODEM_STATE_PRE_HANGUP_ATHSEQ: + buffio_command(m->buffio, ath_sequence); + m->state = MODEM_STATE_HANGUP; + + daemon_log(LOG_INFO, "Delayed HANGUP sequence"); + + /* Don't restart timeout here */ + break; + + case MODEM_STATE_HANGUP: modem_force_hangup(m); - daemon_log(LOG_INFO, "Hang up successful, reinitializing in %i seconds.", RESET_IDLE_TIME); - m->state = MODEM_STATE_IDLE; - modem_timeout(m, RESET_IDLE_TIME); + modem_hangup_seq(m); break; default: break; } + + return 0; } +static int modem_dle_cb(uint8_t c, void *user) { + struct modem *m = user; + assert(m); + + switch (c) { + case ETX : + case DC4 : + daemon_log(LOG_INFO, "Recieved hangup sequence from peer."); + + assert(m->state == MODEM_STATE_CONNECTION); + + buffio_flush_output(m->buffio); + modem_hangup(m, 1); + return -1; + + default: + daemon_log(LOG_INFO, "Recieved DTMF character '%c'", c); + } + return 0; +} + +static void copy_modem_to_child(struct modem *m) { + const uint8_t *sp; + uint8_t *dp; + 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); + + if (sp && sl && dp && dl) { + sl = dle_decode(sp, sl, dp, &dl, modem_dle_cb, m, &m->dle_flag); + + /* It may be the case that dle_decode recieved a + hangup sequence! */ + + if (m->state == MODEM_STATE_CONNECTION) + buffio_read_ptr_inc(m->buffio, sl); + buffio_write_ptr_inc(m->child_buffio, dl); + } +} + +static void copy_child_to_modem(struct modem *m) { + size_t sl, dl; + const uint8_t *sp; + uint8_t *dp; + + assert(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) { + sl = dle_encode(sp, sl, dp, &dl); + buffio_read_ptr_inc(m->child_buffio, sl); + buffio_write_ptr_inc(m->buffio, dl); + } +} + +static int modem_reopen(struct modem *m) { + int fd; + struct termios ts; + assert(m && m->dev); + + if (m->buffio) { + daemon_log(LOG_INFO, "Closing TTY device '%s'."); + buffio_close_output_fd(m->buffio); + buffio_close_input_fd(m->buffio); + } + daemon_log(LOG_INFO, "Trying to open 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)); + goto fail; + } + + daemon_log(LOG_INFO, "TTY device '%s' successfully opened.", m->dev); + + if (tcgetattr(fd, &ts) < 0) { + daemon_log(LOG_ERR, "Failed to get TTY attributes: %s", strerror(errno)); + goto fail; + } + + cfmakeraw(&ts); + + ts.c_cflag |= HUPCL | CS8 | CLOCAL; + ts.c_iflag |= IGNPAR | IGNBRK; + + ts.c_cc[VMIN] = 1; + ts.c_cc[VTIME] = 0; + + cfsetospeed(&ts, BAUD_RATE); + cfsetispeed(&ts, BAUD_RATE); + + tcflush(fd, TCIOFLUSH); + + if (tcsetattr(fd, TCSANOW, &ts) < 0) { + daemon_log(LOG_ERR, "Failed to set TTY attributes: %s", strerror(errno)); + goto fail; + } + + if (!m->buffio) { + m->buffio = buffio_new(fd, fd); + assert(m->buffio); + + 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; + } else + buffio_set_fds(m->buffio, fd, fd); + + return 0; + +fail: + if (fd >= 0) + close(fd); + + return -1; +} diff --git a/src/modem.h b/src/modem.h index a19ff38..320467a 100644 --- a/src/modem.h +++ b/src/modem.h @@ -10,13 +10,16 @@ enum modem_state { MODEM_STATE_CALLER_NUMBER, MODEM_STATE_RING_EXPECT, MODEM_STATE_RING, - MODEM_STATE_ANSWER, + MODEM_STATE_ATA, MODEM_STATE_VTXVRX, MODEM_STATE_CONNECTION, MODEM_STATE_CONNECTION_SHBUF, + MODEM_STATE_PRE_HANGUP_HUPSEQ, + MODEM_STATE_PRE_HANGUP_ATHSEQ, MODEM_STATE_HANGUP, - MODEM_STATE_ATH, - MODEM_STATE_FORCE_HANGUP, + MODEM_STATE_CHILD_DRAIN, /* wait until no further data may/has to be written to the child */ + MODEM_STATE_CHILD_WAIT_EOF, /* wait until no further data may be read from child */ + MODEM_STATE_CHILD_KILL, MODEM_STATE_IDLE }; @@ -30,17 +33,19 @@ struct modem { enum modem_state state; int command_index; + struct buffio *child_buffio; pid_t child_pid; struct timeval timeout; - int dle_flag; char *local_msn; char *ring_number; char *caller_number; struct tabentry *tabentry; + + int dle_flag; }; struct modem *modem_open(const char *dev); diff --git a/src/modemman.c b/src/modemman.c index f7521e3..cccaef9 100644 --- a/src/modemman.c +++ b/src/modemman.c @@ -10,7 +10,7 @@ #include "modem.h" #define MAX_CHANNELS 10 -#define TTY_START 11 +#define TTY_START 12 struct llist { struct modem *modem; diff --git a/src/util.c b/src/util.c index d236253..3ba6506 100644 --- a/src/util.c +++ b/src/util.c @@ -13,34 +13,34 @@ char *basename(char *path) { } ssize_t loop_read(int FILEDES, void *BUFFER, size_t SIZE) { - int c = 0; + ssize_t c = 0; while (SIZE > 0) { - int r = read(FILEDES, BUFFER, SIZE); + ssize_t r = read(FILEDES, BUFFER, SIZE); if (r <= 0) break; SIZE -= r; c += r; - BUFFER = ((void*) (((char*) BUFFER) + r)); + BUFFER += r; } return c; } ssize_t loop_write(int FILEDES, const void *BUFFER, size_t SIZE) { - int c = 0; + ssize_t c = 0; while (SIZE > 0) { - int r = write(FILEDES, BUFFER, SIZE); + ssize_t r = write(FILEDES, BUFFER, SIZE); if (r <= 0) break; SIZE -= r; c += r; - BUFFER = ((void*) (((char*) BUFFER) + r)); + BUFFER += r; } return c; -- cgit