summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJiri Olsa <Jiri Olsa jolsa@redhat.com>2010-03-25 08:42:19 +0100
committerJiri Olsa <Jiri Olsa jolsa@redhat.com>2010-03-25 08:42:19 +0100
commit24e91dc5627b181f90c800cf613614e5787e76da (patch)
tree630eb6e8b18492b5d3c3624ab94be3f3e21bc8c0 /src
downloadtsnif-24e91dc5627b181f90c800cf613614e5787e76da.tar.gz
tsnif-24e91dc5627b181f90c800cf613614e5787e76da.tar.xz
tsnif-24e91dc5627b181f90c800cf613614e5787e76da.zip
initial commit
Diffstat (limited to 'src')
-rw-r--r--src/Makefile27
-rw-r--r--src/fsm.c127
-rw-r--r--src/fsm.h9
-rw-r--r--src/intf.c175
-rw-r--r--src/intf.h80
-rw-r--r--src/list.h82
-rw-r--r--src/sim-pty.c160
-rw-r--r--src/trans-libnl.c195
-rw-r--r--src/trans-libnl.h12
-rw-r--r--src/trans.h41
-rw-r--r--src/tsnif.c207
11 files changed, 1115 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..9cbbfa2
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,27 @@
+
+TSNIF_BIN=tsnif
+TSNIF_OBJS=\
+ src/tsnif.o \
+ src/intf.o \
+ src/fsm.o \
+ src/trans-libnl.o
+TSNIF_LIB=-lnl
+
+OBJS+=$(TSNIF_OBJS)
+PROGRAMS+=$(TSNIF_BIN)
+
+$(TSNIF_BIN): $(TSNIF_OBJS)
+ $(QUIET_LD)$(CC) -o $@ $(TSNIF_OBJS) $(TSNIF_LIB)
+
+install::
+ $(call install,$(TSNIF_BIN),$(bindir),"rx")
+
+SIMPTY_OBJS=\
+ src/sim-pty.o
+
+SIMPTY_BIN=sim-pty
+OBJS+=$(SIMPTY_OBJS)
+PROGRAMS+=$(SIMPTY_BIN)
+
+$(SIMPTY_BIN): $(SIMPTY_OBJS)
+ $(QUIET_LD)$(CC) -o $@ $(SIMPTY_OBJS)
diff --git a/src/fsm.c b/src/fsm.c
new file mode 100644
index 0000000..f56c857
--- /dev/null
+++ b/src/fsm.c
@@ -0,0 +1,127 @@
+
+#include <asm/errno.h>
+
+#include "intf.h"
+#include "trans.h"
+
+
+static int process_release(struct tsnif_handle *h, struct tsnif_term *term)
+{
+ if (!h->ops || !h->ops->cb_release)
+ return -1;
+
+ return h->ops->cb_release(term);
+}
+
+static int process_error(struct tsnif_handle *h, struct tsnif_term *term, int err)
+{
+ if (!h->ops || !h->ops->cb_err)
+ return -1;
+
+ return h->ops->cb_err(term, err);
+}
+
+static int process_tgroup(struct tsnif_handle *h, struct tsnif_term *term,
+ int group)
+{
+ int err;
+
+ err = trans_group(&h->trans, group);
+ term->state = err ? TSNIF_INTF_STATE_DONE : TSNIF_INTF_STATE_DATA;
+
+ TSNIF_DEBUG("adding group %d, err %d\n", group, err);
+ return err;
+}
+
+static int new(struct tsnif_handle *h,
+ struct tsnif_term *term,
+ struct trans_msg *msg)
+{
+ int err = 0;
+
+ switch(msg->cmd) {
+ case TSNIF_CMD_TGROUP:
+ err = process_tgroup(h, term, msg->group);
+ break;
+
+ case TSNIF_CMD_RELEASE:
+ err = process_release(h, term);
+ break;
+
+ case TSNIF_CMD_ATTACH:
+ /* got ATTACH cmd without error ??? */
+ err = -EINVAL;
+ if (msg->err)
+ break;
+
+ err = process_error(h, term, msg->error);
+ break;
+
+ default:
+ return -1;
+ }
+
+ return err;
+}
+
+static int process_data(struct tsnif_handle *h,
+ struct tsnif_term *term,
+ struct trans_msg *msg)
+{
+ struct tsnif_data data;
+
+ TSNIF_DEBUG("type %d, idx %d\n",
+ term->type, term->idx);
+
+ if (!h->ops || !h->ops->cb_data)
+ return -1;
+
+ data.ptr = msg->data.ptr;
+ data.len = msg->data.len;
+ data.flags = msg->data.flags;
+
+ return h->ops->cb_data(term, &data);
+}
+
+static int data(struct tsnif_handle *h,
+ struct tsnif_term *term,
+ struct trans_msg *msg)
+{
+ int err = 0;
+
+ switch(msg->cmd) {
+ case TSNIF_CMD_RELEASE:
+ err = process_release(h, term);
+ break;
+
+ case TSNIF_CMD_DATA:
+ err = process_data(h, term, msg);
+ break;
+
+ default:
+ return -1;
+ }
+
+ return err;
+}
+
+int fsm_process(struct tsnif_handle *h,
+ struct tsnif_term *term,
+ struct trans_msg *msg)
+{
+ TSNIF_DEBUG("got cmd %d for term type %d, idx %d, state %d\n",
+ msg->cmd, term->type, term->idx, term->state);
+
+ switch(term->state) {
+ case TSNIF_INTF_STATE_NEW:
+ return new(h, term, msg);
+
+ case TSNIF_INTF_STATE_DATA:
+ return data(h, term, msg);
+
+ default:
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/fsm.h b/src/fsm.h
new file mode 100644
index 0000000..cca3b5a
--- /dev/null
+++ b/src/fsm.h
@@ -0,0 +1,9 @@
+
+#ifndef FSM_H
+#define FSM_H
+
+int fsm_process(struct tsnif_handle *h,
+ struct tsnif_term *term,
+ struct trans_msg *msg);
+
+#endif /* !FSM_H */
diff --git a/src/intf.c b/src/intf.c
new file mode 100644
index 0000000..fde18a3
--- /dev/null
+++ b/src/intf.c
@@ -0,0 +1,175 @@
+
+#include <asm/errno.h>
+
+#include "intf.h"
+#include "list.h"
+#include "fsm.h"
+
+int tsnif_debug = 0;
+
+static struct tsnif_term *term_find(struct tsnif_handle *h, int type, int idx)
+{
+ struct tsnif_term *t;
+
+ list_for_each_entry(t, &h->terms, list)
+ if ((t->type == type) &&
+ (t->idx == idx))
+ return t;
+
+ return NULL;
+}
+
+static int notify_cb(struct tsnif_handle *h, struct trans_msg *msg)
+{
+ struct tsnif_term term;
+
+ if (!h->ops || !h->ops->cb_notify)
+ return -1;
+
+ memset(&term, 0x0, sizeof(term));
+ term.type = msg->type;
+ term.idx = msg->idx;
+ term.handle = h;
+ term.state = TSNIF_INTF_STATE_NEW;
+
+ return h->ops->cb_notify(&term, msg->cmd);
+}
+
+static int process_mgroup(struct tsnif_handle *h, struct trans_msg *msg)
+{
+ return trans_group(&h->trans, msg->group);
+}
+
+static int trans_cb(struct trans_handle *h, struct trans_msg *msg)
+{
+ struct tsnif_handle *handle;
+ struct tsnif_term *term;
+
+ TSNIF_DEBUG("got cmd %d, type %d, idx %d\n",
+ msg->cmd, msg->type, msg->idx);
+
+ handle = container_of(h, struct tsnif_handle, trans);
+
+ /* then non term related bussines */
+ switch(msg->cmd) {
+ case TSNIF_CMD_TTY_CREATE:
+ case TSNIF_CMD_TTY_RELEASE:
+ return notify_cb(handle, msg);
+
+ case TSNIF_CMD_MGROUP:
+ return process_mgroup(handle, msg);
+ }
+
+ /* and term it is now */
+ term = term_find(handle, msg->type, msg->idx);
+ if (!term)
+ return -ENODEV;
+
+ return fsm_process(handle, term, msg);
+}
+
+static int init_mgroup(struct tsnif_handle *h)
+{
+ struct trans_msg msg;
+
+ msg.cmd = TSNIF_CMD_MGROUP;
+ return trans_send(&h->trans, &msg);
+}
+
+int tsnif_init(struct tsnif_handle *h, struct tsnif_ops *ops)
+{
+ int err;
+
+ if (!ops)
+ return -EINVAL;
+
+ memset(h, 0x0, sizeof(*h));
+ h->ops = ops;
+ INIT_LIST_HEAD(&h->terms);
+
+ err = trans_init(&h->trans, trans_cb);
+ if (err)
+ return err;
+
+ return init_mgroup(h);
+}
+
+int tsnif_close(struct tsnif_handle *h)
+{
+ return trans_close(&h->trans);
+}
+
+int tsnif_process(struct tsnif_handle *h)
+{
+ return trans_process(&h->trans);
+}
+
+int tsnif_fd(struct tsnif_handle *h)
+{
+ return trans_fd(&h->trans);
+}
+
+int tsnif_term_add(struct tsnif_handle *h, struct tsnif_term *term,
+ int type, int idx)
+{
+ struct tsnif_term *t;
+
+ t = term_find(h, type, idx);
+ if (t)
+ return -EEXIST;
+
+ memset(term, 0x0, sizeof(*term));
+ term->type = type;
+ term->idx = idx;
+ term->handle = h;
+ term->state = TSNIF_INTF_STATE_NEW;
+
+ list_add_tail(&term->list, &h->terms);
+ return 0;
+}
+
+int tsnif_term_del(struct tsnif_handle *h, struct tsnif_term *term)
+{
+ list_del(&term->list);
+ return 0;
+}
+
+int tsnif_attach(struct tsnif_term *term)
+{
+ struct trans_msg msg;
+ struct tsnif_handle *h = term->handle;
+
+ TSNIF_DEBUG("type %d, idx %d\n", term->type, term->idx);
+
+ if (term->state != TSNIF_INTF_STATE_NEW) {
+ TSNIF_DEBUG("wrong state (%d) skiping detach\n",
+ term->state);
+ return -1;
+ }
+
+ msg.type = term->type;
+ msg.idx = term->idx;
+ msg.cmd = TSNIF_CMD_ATTACH;
+
+ return trans_send(&h->trans, &msg);
+}
+
+int tsnif_detach(struct tsnif_term *term)
+{
+ struct trans_msg msg;
+ struct tsnif_handle *h = term->handle;
+
+ TSNIF_DEBUG("type %d, idx %d\n", term->type, term->idx);
+
+ if (term->state != TSNIF_INTF_STATE_DATA) {
+ TSNIF_DEBUG("wrong state (%d) skiping detach\n",
+ term->state);
+ return -1;
+ }
+
+ msg.type = term->type;
+ msg.idx = term->idx;
+ msg.cmd = TSNIF_CMD_DETACH;
+
+ return trans_send(&h->trans, &msg);
+}
diff --git a/src/intf.h b/src/intf.h
new file mode 100644
index 0000000..a40e9c4
--- /dev/null
+++ b/src/intf.h
@@ -0,0 +1,80 @@
+
+#ifndef INTF_H
+#define INTF_H
+
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <linux/tsnif.h>
+
+#include "list.h"
+#include "trans.h"
+
+enum {
+ TSNIF_INTF_STATE_NEW = 1,
+ TSNIF_INTF_STATE_DATA,
+ TSNIF_INTF_STATE_DONE,
+};
+
+struct tsnif_term {
+ int type;
+ int idx;
+ int state;
+
+ struct list_head list;
+ struct tsnif_handle *handle;
+};
+
+struct tsnif_data {
+ void *ptr;
+ int len;
+ uint flags;
+};
+
+typedef int(*cb_data_t)(struct tsnif_term *term, struct tsnif_data *data);
+typedef int(*cb_err_t)(struct tsnif_term *term, int err);
+typedef int(*cb_release_t)(struct tsnif_term *term);
+typedef int(*cb_notify_t)(struct tsnif_term *term, int action);
+
+struct tsnif_ops {
+ cb_data_t cb_data;
+ cb_err_t cb_err;
+ cb_release_t cb_release;
+ cb_notify_t cb_notify;
+};
+
+struct tsnif_handle {
+ struct list_head terms;
+ struct tsnif_ops *ops;
+ struct trans_handle trans;
+};
+
+/* handle functions */
+int tsnif_init(struct tsnif_handle *h, struct tsnif_ops *ops);
+int tsnif_close(struct tsnif_handle *h);
+int tsnif_process(struct tsnif_handle *h);
+int tsnif_fd(struct tsnif_handle *h);
+
+/* term functions */
+int tsnif_term_add(struct tsnif_handle *h, struct tsnif_term *term,
+ int type, int idx);
+int tsnif_term_del(struct tsnif_handle *h, struct tsnif_term *term);
+int tsnif_attach(struct tsnif_term *term);
+int tsnif_detach(struct tsnif_term *term);
+
+extern int tsnif_debug;
+
+#define TSNIF_DEBUG(fmt, args...) \
+ do { \
+ if (tsnif_debug) { \
+ char lpbuf[256]; \
+ snprintf(lpbuf, sizeof(lpbuf)-1, "USER [%3d:%s@%s:%05d] %s", \
+ (pid_t) syscall(SYS_gettid), \
+ __FUNCTION__, \
+ __FILE__, \
+ __LINE__, \
+ fmt); \
+ printf(lpbuf, ## args); \
+ } \
+ } while(0)
+
+#endif /* !INTF_H */
diff --git a/src/list.h b/src/list.h
new file mode 100644
index 0000000..4590b33
--- /dev/null
+++ b/src/list.h
@@ -0,0 +1,82 @@
+#ifndef LIST_H
+#define LIST_H
+
+struct list_head
+{
+ struct list_head * next;
+ struct list_head * prev;
+};
+
+static inline void INIT_LIST_HEAD(struct list_head *list)
+{
+ list->next = list;
+ list->prev = list;
+}
+
+static inline void __list_add(struct list_head *obj,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ prev->next = obj;
+ obj->prev = prev;
+ next->prev = obj;
+ obj->next = next;
+}
+
+static inline void list_add_tail(struct list_head *obj,
+ struct list_head *head)
+{
+ __list_add(obj, head->prev, head);
+}
+
+static inline void list_add_head(struct list_head *obj,
+ struct list_head *head)
+{
+ __list_add(obj, head, head->next);
+}
+
+static inline void list_del(struct list_head *obj)
+{
+ obj->next->prev = obj->prev;
+ obj->prev->next = obj->next;
+}
+
+static inline int list_empty(struct list_head *head)
+{
+ return head->next == head;
+}
+
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - ((size_t) &((type *)0)->member));})
+
+#define list_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+
+#define list_at_tail(pos, head, member) \
+ ((pos)->member.next == (head))
+
+#define list_at_head(pos, head, member) \
+ ((pos)->member.prev == (head))
+
+#define LT_LIST_HEAD(name) \
+ struct list_head name = { &(name), &(name) }
+
+#define list_first_entry(head, type, member) \
+ list_entry((head)->next, type, member)
+
+#define list_for_each_entry(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member); \
+ &(pos)->member != (head); \
+ (pos) = list_entry((pos)->member.next, typeof(*(pos)), member))
+
+#define list_for_each_entry_safe(pos, n, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &(pos)->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+#define init_list_head(head) \
+ do { (head)->next = (head); (head)->prev = (head); } while (0)
+
+#endif
diff --git a/src/sim-pty.c b/src/sim-pty.c
new file mode 100644
index 0000000..dc46e6b
--- /dev/null
+++ b/src/sim-pty.c
@@ -0,0 +1,160 @@
+
+#define _XOPEN_SOURCE
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <getopt.h>
+
+int readonly = 0;
+int verbose = 0;
+int no_sleep = 0;
+int sleep_us = 100;
+
+
+int write_pts(void)
+{
+ int mfd, sfd;
+ char *sname;
+ u_int i;
+
+ if ((mfd = getpt()) < 0) {
+ perror("getpt failed");
+ return -1;
+ }
+
+ if (unlockpt(mfd) != 0) {
+ perror("unlockpt failed");
+ return -1;
+ }
+
+ if ((sname = (char*)ptsname(mfd)) == NULL) {
+ perror("ptsname");
+ return -1;
+ }
+
+ printf("got slave pty %s\n", sname);
+
+ if ((sfd = open(sname, O_RDWR)) < 0) {
+ perror("open failed");
+ return -1;
+ }
+
+ fcntl(sfd, F_SETFL, O_NONBLOCK);
+
+ for(i = 0; ; i++) {
+ char buf[10000];
+ int len;
+
+ if (!readonly) {
+ sprintf(buf, "kravaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%03ddebil\n", i);
+
+ if (-1 == write(sfd, buf, strlen(buf))) {
+ perror("write failed");
+ return -1;
+ }
+ }
+
+ buf[0] = 0x0;
+
+ if (-1 == (len = read(mfd, buf, sizeof(buf)))) {
+ perror("write failed");
+ return -1;
+ }
+ buf[len] = 0x0;
+
+ if (verbose)
+ printf("master %s got: [%s]\n", sname, buf);
+
+ if (!no_sleep)
+ usleep(sleep_us);
+ }
+
+ close(mfd);
+
+ return 0;
+}
+
+int write_file(char *name)
+{
+ int fd;
+ u_int i;
+
+ if ((fd = open(name, O_RDWR)) < 0) {
+ perror("open failed");
+ return -1;
+ }
+
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+
+ for(i = 0; ; i++) {
+ char buf[10000];
+
+ if (!readonly) {
+ sprintf(buf, "kravaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%03d\n", i);
+
+ if (-1 == write(fd, buf, strlen(buf))) {
+ perror("write failed");
+ return -1;
+ }
+ }
+
+ if (!no_sleep)
+ usleep(sleep_us);
+ }
+
+ close(fd);
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ char *file = NULL;
+
+ while (1) {
+ int c;
+ int option_index = 0;
+ static struct option long_options[] = {
+ {"read", no_argument , 0, 'r'},
+ {"verbose", no_argument , 0, 'v'},
+ {"f", no_argument , 0, 'f'},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long(argc, argv, "rvns:f:", long_options,
+ &option_index);
+
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'r':
+ readonly = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'n':
+ no_sleep = 1;
+ break;
+ case 's':
+ sleep_us = atoi(optarg);
+ break;
+ case 'f':
+ file = optarg;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (file)
+ return write_file(file);
+
+ return write_pts();
+}
diff --git a/src/trans-libnl.c b/src/trans-libnl.c
new file mode 100644
index 0000000..e692cd9
--- /dev/null
+++ b/src/trans-libnl.c
@@ -0,0 +1,195 @@
+
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#include <linux/tsnif.h>
+#include <asm/errno.h>
+
+#include "intf.h"
+#include "trans.h"
+
+TSNIF_POLICY(gnl_policy);
+
+static int ack_cb(struct nl_msg *msg, void *arg)
+{
+ return NL_OK;
+}
+
+static int seq_cb(struct nl_msg *msg, void *arg)
+{
+ /* FIXME Our sequence numbers of DATA packets are zero,
+ * so let disable the seq check completely */
+ return NL_OK;
+}
+
+#define CB_RET(err) (err ? NL_STOP : NL_OK)
+
+int err_cb(struct sockaddr_nl *nla, struct nlmsgerr *nlerr, void *arg)
+{
+ struct nlmsghdr *nlh = &nlerr->msg;
+ struct genlmsghdr *ghdr = nlmsg_data(nlh);
+ struct nlattr *attrs[TSNIF_ATTR_MAX + 1];
+ struct trans_handle *h = arg;
+ struct trans_msg m;
+
+ genlmsg_parse(nlh, 0, attrs, TSNIF_ATTR_MAX, gnl_policy);
+ memset(&m, 0x0, sizeof(m));
+
+ if (attrs[TSNIF_ATTR_IDX])
+ m.idx = nla_get_u32(attrs[TSNIF_ATTR_IDX]);
+
+ if (attrs[TSNIF_ATTR_TYPE])
+ m.type = nla_get_u32(attrs[TSNIF_ATTR_TYPE]);
+
+ m.cmd = ghdr->cmd;
+ m.err = 1;
+ m.error = nlerr->error;
+
+ TSNIF_DEBUG("got err %d for cmd %d, type %d, idx %d\n",
+ nlerr->error, m.cmd, m.idx, m.type);
+
+ return CB_RET(h->cb(h, &m));
+}
+
+static int parse_cb(struct nl_msg *msg, void *arg)
+{
+ struct nlmsghdr *nlh = nlmsg_hdr(msg);
+ struct genlmsghdr *ghdr = nlmsg_data(nlh);
+ struct nlattr *attrs[TSNIF_ATTR_MAX + 1];
+ struct trans_handle *h = arg;
+ struct trans_msg m;
+
+ genlmsg_parse(nlh, 0, attrs, TSNIF_ATTR_MAX, gnl_policy);
+ memset(&m, 0x0, sizeof(m));
+
+ if (attrs[TSNIF_ATTR_IDX])
+ m.idx = nla_get_u32(attrs[TSNIF_ATTR_IDX]);
+
+ if (attrs[TSNIF_ATTR_TYPE])
+ m.type = nla_get_u32(attrs[TSNIF_ATTR_TYPE]);
+
+ m.cmd = ghdr->cmd;
+
+ TSNIF_DEBUG("got cmd %d\n", m.cmd);
+
+ switch(ghdr->cmd) {
+ case TSNIF_CMD_RELEASE:
+ case TSNIF_CMD_TTY_CREATE:
+ case TSNIF_CMD_TTY_RELEASE:
+ /* nothing to do, but not an error */
+ break;
+
+ case TSNIF_CMD_MGROUP:
+ case TSNIF_CMD_TGROUP:
+ m.group = nla_get_u32(attrs[TSNIF_ATTR_GROUP]);
+ break;
+
+ case TSNIF_CMD_DATA:
+ {
+ m.data.ptr = nla_data(attrs[TSNIF_ATTR_DATA]);
+ m.data.len = nla_len(attrs[TSNIF_ATTR_DATA]);
+ m.data.flags = nla_get_u32(attrs[TSNIF_ATTR_FLAGS]);
+ break;
+ }
+
+ default:
+ /* we are not supposed to get anything else */
+ return -1;
+ }
+
+ return CB_RET(h->cb(h, &m));
+}
+
+int trans_init(struct trans_handle *h, trans_cb_t cb)
+{
+ struct nl_handle *sock;
+ int family;
+ int err;
+
+ memset(h, 0x0, sizeof(*h));
+
+ sock = nl_handle_alloc();
+ if (!sock)
+ return nl_get_errno();
+
+ err = genl_connect(sock);
+ if (err)
+ return err;
+
+ TSNIF_DEBUG("connected\n");
+
+ family = genl_ctrl_resolve(sock, "tsnif");
+ if (family < 0)
+ return family;
+
+ TSNIF_DEBUG("tsnif module found\n");
+
+ nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, parse_cb, h);
+ nl_socket_modify_cb(sock, NL_CB_SEND_ACK, NL_CB_CUSTOM, ack_cb, h);
+ nl_socket_modify_cb(sock, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, seq_cb, h);
+ nl_cb_err(nl_socket_get_cb(sock), NL_CB_CUSTOM, err_cb, h);
+
+ h->sock = sock;
+ h->family = family;
+ h->cb = cb;
+ return 0;
+}
+
+int trans_close(struct trans_handle *h)
+{
+ nl_handle_destroy(h->sock);
+ return 0;
+}
+
+int trans_process(struct trans_handle *h)
+{
+ int err;
+
+ err = nl_recvmsgs_default(h->sock);
+ if (err)
+ TSNIF_DEBUG("got error: %s\n", nl_geterror());
+
+ return err;
+}
+
+int trans_send(struct trans_handle *h, struct trans_msg *m)
+{
+ struct nl_msg *msg;
+ int err;
+
+ TSNIF_DEBUG("sending cmd = %d\n", m->cmd);
+
+ if ((m->cmd != TSNIF_CMD_ATTACH) &&
+ (m->cmd != TSNIF_CMD_DETACH) &&
+ (m->cmd != TSNIF_CMD_MGROUP))
+ return -EINVAL;
+
+ msg = nlmsg_alloc();
+ genlmsg_put(msg,
+ NL_AUTO_PID, NL_AUTO_SEQ,
+ h->family, 0, NLM_F_ECHO,
+ m->cmd, TSNIF_VERSION);
+
+ if ((m->cmd == TSNIF_CMD_ATTACH) ||
+ (m->cmd == TSNIF_CMD_DETACH)) {
+ nla_put_u32(msg, TSNIF_ATTR_TYPE, m->type);
+ nla_put_u32(msg, TSNIF_ATTR_IDX, m->idx);
+ }
+
+ err = nl_send_auto_complete(h->sock, msg);
+ nlmsg_free(msg);
+
+ TSNIF_DEBUG("sent result %d\n", err);
+ return err > 0 ? 0 : err;
+}
+
+int trans_group(struct trans_handle *h, int group)
+{
+ return nl_socket_add_membership(h->sock, group);
+}
+
+int trans_fd(struct trans_handle *h)
+{
+ return nl_socket_get_fd(h->sock);
+}
diff --git a/src/trans-libnl.h b/src/trans-libnl.h
new file mode 100644
index 0000000..91ec8d2
--- /dev/null
+++ b/src/trans-libnl.h
@@ -0,0 +1,12 @@
+
+#ifndef TRANS_LIBNL_H
+#define TRANS_LIBNL_H
+
+struct trans_handle {
+ struct nl_handle *sock;
+ int family;
+
+ trans_cb_t cb;
+};
+
+#endif /* !TRANS_LIBNL_H */
diff --git a/src/trans.h b/src/trans.h
new file mode 100644
index 0000000..38589d4
--- /dev/null
+++ b/src/trans.h
@@ -0,0 +1,41 @@
+
+#ifndef TRANS_H
+#define TRANS_H
+
+struct trans_handle;
+struct trans_msg;
+
+typedef int(*trans_cb_t)(struct trans_handle *h, struct trans_msg *msg);
+
+struct trans_msg {
+ int cmd;
+ int type;
+ int idx;
+ int err;
+
+ union {
+ /* data message */
+ struct {
+ void *ptr;
+ int len;
+ uint flags;
+ } data;
+ /* notify message */
+ int action;
+ /* error message */
+ int error;
+ /* group message */
+ int group;
+ };
+};
+
+int trans_init(struct trans_handle *h, trans_cb_t cb);
+int trans_close(struct trans_handle *h);
+int trans_process(struct trans_handle *h);
+int trans_send(struct trans_handle *h, struct trans_msg *msg);
+int trans_group(struct trans_handle *h, int group);
+int trans_fd(struct trans_handle *h);
+
+#include "trans-libnl.h"
+
+#endif /* !TRANS_H */
diff --git a/src/tsnif.c b/src/tsnif.c
new file mode 100644
index 0000000..9330d15
--- /dev/null
+++ b/src/tsnif.c
@@ -0,0 +1,207 @@
+
+#include <stdio.h>
+#include <sys/select.h>
+#include <unistd.h>
+#include <termios.h>
+#include <getopt.h>
+#include <errno.h>
+#include <setjmp.h>
+#include <signal.h>
+
+#include "autoconf.h"
+#include "intf.h"
+
+struct tsnif_handle handle;
+struct tsnif_term term;
+
+static int killed = 0;
+static int type = -1;
+static int idx = -1;
+
+
+static int data_cb(struct tsnif_term* term, struct tsnif_data *data)
+{
+ if (data->flags == TSNIF_FLAGS_PTY_SLAVE) {
+ fwrite(data->ptr, data->len, 1, stdout);
+ fflush(NULL);
+ }
+
+ return 0;
+}
+
+static int err_cb(struct tsnif_term *term, int err)
+{
+ return err;
+}
+
+static int release_cb(struct tsnif_term *term)
+{
+ return -1;
+}
+
+static int notify_cb(struct tsnif_term *term, int action)
+{
+ return 0;
+}
+
+struct tsnif_ops ops = {
+ .cb_data = data_cb,
+ .cb_err = err_cb,
+ .cb_release = release_cb,
+ .cb_notify = notify_cb,
+};
+
+static void usage()
+{
+ printf("tsnif -t <terminal type> -i <terminal index>\n");
+ printf(" where types can be 'tty', 'ttys' or 'pty'\n");
+ _exit(-1);
+}
+
+static void sig_handler(int sig)
+{
+ printf("killed\n");
+ killed = 1;
+}
+
+int set_term(int set_init)
+{
+ struct termios new_settings;
+ static struct termios init_settings;
+
+ if (set_init)
+ return tcsetattr(0, TCSANOW, &init_settings);
+
+ tcgetattr(0, &init_settings);
+ new_settings = init_settings;
+ new_settings.c_lflag &= ~ICANON;
+ new_settings.c_lflag &= ~ECHO;
+
+ return tcsetattr(0, TCSANOW, &new_settings);
+}
+
+static int get_type(char *optarg)
+{
+ int i;
+ char *types[TSNIF_TYPE_MAX] = {
+ "tty", "ttys", "pty",
+ };
+
+ for(i = 0; i < TSNIF_TYPE_MAX; i++)
+ if (!strcmp(optarg, types[i]))
+ break;
+
+ return i;
+}
+
+static int get_args(int argc, char **argv)
+{
+ while (1) {
+ int c;
+ int option_index = 0;
+ static struct option long_options[] = {
+ {"type", required_argument, 0, 't'},
+ {"idx", required_argument, 0, 'i'},
+ {"version", no_argument, 0, 'V'},
+ {"help", no_argument, 0, 'h'},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long(argc, argv, "t:i:vh",
+ long_options, &option_index);
+
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 't':
+ type = get_type(optarg);
+ if (type == TSNIF_TYPE_MAX)
+ return -1;
+ break;
+
+ case 'i':
+ errno = 0;
+ idx = strtol(optarg, (char **) NULL, 10);
+ if (errno) {
+ perror("strtol failed");
+ printf("strtol failed\n");
+ return -1;
+ }
+ break;
+
+ case 'v':
+ printf("tsnif "CONFIG_TSNIF_VER"\n");
+ break;
+
+ case 'h':
+ usage();
+ break;
+
+ default:
+ printf("unknown option '%c'", c);
+ } /* switch(c) */
+ } /* while(1) */
+
+ if ((type == -1) || (idx == -1))
+ return -1;
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int err, ret;
+ jmp_buf env;
+
+ if (get_args(argc, argv))
+ usage();
+
+ err = tsnif_init(&handle, &ops);
+ if (err)
+ return err;
+
+ if ((ret = setjmp(env))) {
+ if (ret > 1) {
+ tsnif_detach(&term);
+ set_term(1);
+ }
+
+ tsnif_close(&handle);
+ printf("done err = %d\n", err);
+ return err;
+ }
+
+ err = tsnif_term_add(&handle, &term, type, idx);
+ if (err)
+ longjmp(env, 1);
+
+ err = tsnif_attach(&term);
+ if (err)
+ longjmp(env, 1);
+
+
+ set_term(0);
+ signal(SIGINT, sig_handler);
+
+ while(!killed) {
+ fd_set rfds;
+ struct timeval tv = { 1, 0};
+ int ts_fd = tsnif_fd(&handle), ret;
+
+ FD_ZERO(&rfds);
+ FD_SET(ts_fd, &rfds);
+
+ ret = select(ts_fd + 1, &rfds, NULL, NULL, &tv);
+ if (ret == -1) {
+ perror("select()");
+ continue;
+ } else if (!ret)
+ continue;
+
+ if (tsnif_process(&handle))
+ longjmp(env, 2);
+ }
+
+ longjmp(env, 2);
+}