From d918d9b2c4fc9eaa58bcd9a266a6a0330c88391c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Dec 2003 00:20:19 +0000 Subject: some more work git-svn-id: file:///home/lennart/svn/public/ivam2/trunk@6 dbf6933d-3bce-0310-9bcc-ed052ba35b35 --- conf/msntab | 8 ++ src/exec.c | 271 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/exec.h | 11 +++ src/main.c | 4 + src/modem.c | 84 +++++++++++++++++-- src/modem.h | 8 +- 6 files changed, 378 insertions(+), 8 deletions(-) create mode 100644 conf/msntab create mode 100644 src/exec.c create mode 100644 src/exec.h diff --git a/conf/msntab b/conf/msntab new file mode 100644 index 0000000..ca16ef7 --- /dev/null +++ b/conf/msntab @@ -0,0 +1,8 @@ + +# local MSN remote MSN options action + +41264179 41264179 rings=0 ivam-say /var/spool/ivam/hello.ulaw +41264179 41264177 rings=0 ivam-dialup --pin=4711 ppp0 +* 41264179 rings=0 @hangup +41264179 * rings=2,shbuf ivam-voicebox --pin=4711 +* * defaults @ignore diff --git a/src/exec.c b/src/exec.c new file mode 100644 index 0000000..d9d8e49 --- /dev/null +++ b/src/exec.c @@ -0,0 +1,271 @@ +#include "exec.h" +#include "main.h" + +#define CHILD_BUF_SIZE 1024 + +struct process_info { + pid_t pid; + int killed; + int child_pipe; + process_exit_cb_t *cb; + void *user; + struct process_info *next; + + char *read_buf; + size_t read_buf_size; +}; + +static struct process_info *procs = NULL; + +static struct process_info *find_process(pid_t pid) { + struct process_info *p = procs; + + while (p) { + if (p->pid == pid) + return p; + + p = p->next; + } + + return NULL; +} + +static void *oop_read_cb(oop_source *source, int fd, oop_event event, void *user); + +static void close_child_pipe(struct process_info *p) { + assert(p); + + if (p->read_buf_len && p->read_buf) { + *(p->read_buf + p->read_buf_len) = 0; + daemon_log(LOG_INFO, "child(%lu): %s", (unsigned long) p->pid, p->read_buf); + p->read_buf_len = 0; + } + + if (p->child_pipe >= 0) { + assert(event_source && event_source->cancel_fd); + event_source->cancel_fd(event_source, p->child_pipe, OOP_READ, oop_read_cb, p); + close(p->child_pipe); + p->child_pipe = -1; + } +} + + +static void free_process_info(struct process_info *p) { + assert (p); + close_child_pipe(p); + + if (p->read_buf) + free(p->read_buf); + free(p); +} + +static void remove_process(pid_t pid) { + struct process_info *p; + + if (!procs) + return; + + if (procs->pid == pid) { + p = procs; + procs = procs->next; + free(p); + return; + } + + p = procs; + while (p->next) { + if (p->next->pid == pid) { + struct process_info *r; + r = p->next; + p->next = p->next->next; + free_process_info(r); + } + p = p->next; + } + + return; +} + +static void *oop_sigchld_cb(oop_source *source, int sig, void *user) { + pid_t pid; + int status; + struct process_info *p; + assert(source && sig == SIGCHLD); + + if ((pid = wait(&status)) <= 0) { + daemon_log(LOG_ERR, "wait() failed: %s", strerror(errno)); + return OOP_HALT; + } + + if (!(p = find_process_by_pid(pid))) { + daemon_log(LOG_WARN, "Got SIGCHLD for unknown process, reaping"); + return OOP_CONTINUE; + } + + assert(p && p->cb); + + if (!p->killed) + p->cb(pid, status, p->user); + + remove_process(pid); + + return OOP_CONTINUE; +} + + +static void *oop_read_cb(oop_source *source, int fd, oop_event event, void *user) { + struct process_info *p; + ssize_t s; + char *c, *b, *start; + size_t i; + assert(source && event == OOP_READ); + + p = (struct process_info*) user; + assert(p && p->child_pipe == fd && fd >= 0); + + if (!p->read_buf) { + p->read_buf = malloc(CHILD_BUF_SIZE); + p->read_buf_len = 0; + } + + assert(p->read_buf); + + start = p->read_buf+p->read_buf_len; + if ((s = read(p->child_pipe, start, CHILD_BUF_SIZE-p->read_buf_len-1)) < 0) { + daemon_log(LOG_ERR, "Failed to read from child pipe: %s", strerror(errno)); + return OOP_HALT; + } + + if (s == 0) { /* EOF */ + close_child_pipe(p); + return OOP_CONTINUE; + } + + /* Escape */ + for (c = start, i = 0; i < s; i++, c++) + if (*c < 32 || *c == 127) + *c = '.'; + + + p->read_buf_len += s; + + for (c = b = p->read_buf, i = 0; i < p->read_buf_len; i++, c++) { + if (*c == '\r' || *c == '\n') { + if (c > b) { + *c = 0; + daemon_log(LOG_INFO, "child: %s", b); + } + b = c+1; + } + } + + if (b != p->read_buf) + memcpy(p->read_buf, b, p->read_buf_len = c-b); + + return OOP_CONTINUE; +} + + +pid_t child_process_create(const char *file, char *const argv[], process_exit_cb_t cb, void*user) { + pid_t pid; + int fds[2]; + + if (pipe(fds) < 0) { + daemon_log(LOG_ERR, "pipe() failed: %s", strerror(errno)); + return -1; + } + + if ((pid = fork()) < 0) { + daemon_log(LOG_ERR, "fork() failed: %s", strerror(errno)); + close(fds[0]); + close(fds[1]); + return -1; + } + + if (pid) { + /* parent */ + + struct process_info *p = malloc(sizeof(struct process_info)); + assert(p); + memset(p, 0, sizeof(struct process_info)); + + p->pid = pid; + p->cb = cb; + p->user = user; + p->next = procs; + p->killed = 0; + + p->client_pipe = fds[0]; + close(fds[1]); + + assert(event_source && event_source->on_fd); + event_source->on_fd(event_source, p->client_pipe, oop_read_cb, p); + + procs = p; + + return pid; + + } else { + /* child */ + + int fd; + + close(fds[0]); + for (fd = 0; fd <= 2; fd++) { + if (fd != fds[1]) + close(fd); + } + + if ((fds[1] != 1 && dup2(fds[1], 1) < 0) || + (fds[1] != 2 && dup2(fds[1], 2) < 0)) { + daemon_log(LOG_ERR, "dup2() failed: %s", strerror(errno)); + exit(1); + } + + if (fds[1] != 1 && fds[1] != 2) + close(fds[1]); + + if (open("/dev/null", O_RDONLY) != 0) { + daemon_log(LOG_ERR, "open(\"/dev/null\") failed: %s", strerror(errno)); + exit(1); + } + + execv(file, argv); + daemon_log(LOG_ERR, "exec() failed: %s", strerror(errno)); + exit(1); + } +} + + + +int child_process_init(void) { + assert(!procs); + assert(event_source && event_source->on_signal); + event_source->on_signal(event_source, SIGCHLD, oop_sigchld_cb, NULL); + return 0; +} + +void child_process_done(void) { + assert(event_source && event_source->cancel_signal); + event_source->cancel_signal(event_source, SIGCHLD, oop_sigchld_cb, NULL); + + while (procs) { + struct process_info *p = procs; + procs = procs->next; + free_process_info(p); + } +} + + +int child_process_kill(pid_t pid) { + struct process_info *p = find_process(pid); + assert (p); + + if (kill(p->pid, SIGTERM) < 0 && errno != ESRCH) { + daemon_log(LOG_ERR, "Failed to kill() process: %s", strerror(errno)); + return -1; + } + + p->killed = 1; + return 0; +} diff --git a/src/exec.h b/src/exec.h new file mode 100644 index 0000000..a08b838 --- /dev/null +++ b/src/exec.h @@ -0,0 +1,11 @@ +#ifndef fooexechfoo +#define fooexechfoo + +typedef void (*process_exit_cb_t) (pid_t pid, int status, void *user); + +pid_t child_process_create(const char *file, char *const argv[], process_exit_cb_t cb, void *user); +int child_process_kill(pid_t pid); +int child_process_init(void); +void child_process_done(void); + +#endif diff --git a/src/main.c b/src/main.c index 9fac796..1d0d788 100644 --- a/src/main.c +++ b/src/main.c @@ -18,6 +18,9 @@ int main_loop(void) { event_source = oop_sys_source(sys); assert(event_source); + if (child_process_init() < 0) + goto finish; + if (modem_manager_init(CHANNELS) < 0) goto finish; @@ -30,6 +33,7 @@ int main_loop(void) { finish: modem_manager_done(); + child_process_done(); if (sys) { event_source = NULL; diff --git a/src/modem.c b/src/modem.c index 44eec06..0d47e4d 100644 --- a/src/modem.c +++ b/src/modem.c @@ -55,6 +55,7 @@ struct modem *modem_open(const char *dev) { assert(m->dev); m->fd = fd; m->state = MODEM_STATE_INIT; + m->child_pid = -1; return m; @@ -70,25 +71,94 @@ fail: void modem_close(struct modem *m) { assert(m); - device_unlock(m->dev); + if (m->child_pid != -1) + child_process_kill(m->child_pid); + close(m->fd); + device_unlock(m->dev); free(m->dev); free(m); } -static void* modem_cb(oop_source *source, int fd, oop_event event, void *user) { - struct modem *m; +static int modem_read(struct modem *m) { + char *p; + ssize_t s; + assert(m && m->input_buf); + + p = m->input_buf+m->input_buf_len; + if ((s = read(m->fd, &p, MODEM_BUF_LEN-m->input_buf_len)) <= 0) { + daemon_log(LOG_ERR, "Failed to read() from modem: %s", !s ? "EOF" : strerror(errno)); + return -1; + } + + m->input_buf_len += s; +} + +static void* oop_timeout_cb(oop_source *source,struct timeval tv,void *user) { + struct modem *m = (struct modem*) user; + assert(source && user); + + daemon_log(LOG_ERR, "Timeout reached for device <%s>", m->device); + return OOP_HALT; +} + +static void modem_timeout(struct modem *m, int t) { + struct timeval tv = { t, 0 }; + assert(m); + assert(event_source && event_source->on_time); + event_source->cancel_time(event_source, m->timeout, oop_timeout_cb, m); + + if (t > 0) { + gettimeofday(&m->timeout); + m->tv_sec += t; + + assert(event_source && event_source->on_time); + event_source->on_time(event_source, m->timeout, oop_timeout_cb, m); + } +} + +static void* oop_init_cb(oop_source *source, int fd, oop_event event, void *user) { + struct modem *m = (struct modem*) user; assert(source && user); - m = (struct modem*) user; + assert(m && m->fd == fd && m->mode == MODEM_STATE_INIT); + + if (event == OOP_READ) { + if (modem_read(m) < 0) + return OOP_HALT; + + } else if (event == OOP_WRITE) { + modem_write(m); + } + + return OOP_CONTINUE; +} - if (m->mode == MODEM_STATE_INIT) { - } else if (m->mode == MODEM_STATE_AUDIO) { +static void* oop_audio_simple_cb(oop_source *source, int fd, oop_event event, void *user) { + struct modem *m = (struct modem*) user; + assert(source && user); + assert(m && m->mode == MODEM_STATE_AUDIO_SIMPLE); - if (event) + if (event == OOP_READ) { + modem_read(m); + } else if (event == OOP_WRITE) { + modem_write(m); } + + return OOP_CONTINUE; +} +static void* oop_audio_shbuf_cb(oop_source *source, int fd, oop_event event, void *user) { + struct modem *m = (struct modem*) user; + assert(source && user); + assert(m && m->mode == MODEM_STATE_AUDIO_SHBUF); + + if (event == OOP_READ) { + modem_read(m); + } else if (event == OOP_WRITE) { + modem_write(m); + } return OOP_CONTINUE; } diff --git a/src/modem.h b/src/modem.h index edcfefd..7383d1c 100644 --- a/src/modem.h +++ b/src/modem.h @@ -1,7 +1,9 @@ #ifndef foomodemhfoo #define foomodemhfoo -enum modem_state { MODEM_STATE_INIT, MODEM_STATE_AUDIO }; +enum modem_state { MODEM_STATE_INIT, MODEM_STATE_AUDIO_SIMPLE, MODEM_STATE_AUDIO_SHBUF, MODEM_STATE_DONE }; + +#define MODEM_BUF_LEN (10*1024) struct modem { char *dev; @@ -14,6 +16,10 @@ struct modem { size_t input_buf_len; enum modem_state state; + + pid_t child_pid; + + struct timeval timeout; }; struct modem *modem_open(const char *dev); -- cgit