summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2003-12-17 00:20:19 +0000
committerLennart Poettering <lennart@poettering.net>2003-12-17 00:20:19 +0000
commitd918d9b2c4fc9eaa58bcd9a266a6a0330c88391c (patch)
treec64655d3dcb31e4c723b92af2fc3cd9ebc54dfe2
parentc35f713ae46fef0bf5d5dbab434c2f581e1d1181 (diff)
some more work
git-svn-id: file:///home/lennart/svn/public/ivam2/trunk@6 dbf6933d-3bce-0310-9bcc-ed052ba35b35
-rw-r--r--conf/msntab8
-rw-r--r--src/exec.c271
-rw-r--r--src/exec.h11
-rw-r--r--src/main.c4
-rw-r--r--src/modem.c84
-rw-r--r--src/modem.h8
6 files changed, 378 insertions, 8 deletions
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);