summaryrefslogtreecommitdiffstats
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
downloadtsnif-24e91dc5627b181f90c800cf613614e5787e76da.tar.gz
tsnif-24e91dc5627b181f90c800cf613614e5787e76da.tar.xz
tsnif-24e91dc5627b181f90c800cf613614e5787e76da.zip
initial commit
-rw-r--r--Makefile115
-rw-r--r--configure.ac20
-rw-r--r--include/autoconf.h.in9
-rw-r--r--include/autoconf.make.in19
-rw-r--r--include/linux/tsnif.h62
-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
16 files changed, 1340 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..577e904
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,115 @@
+
+export TSNIF_INCLUDE = $(CURDIR)/include
+export TSNIF_AUTOCONF = $(TSNIF_INCLUDE)/autoconf.make
+include $(TSNIF_AUTOCONF)
+
+# looks like DESTDIR is a standard, but prioritize ROOTDIR anyway
+ifdef DESTDIR
+ifndef ROOTDIR
+ROOTDIR=$(DESTDIR)
+endif
+endif
+
+
+ifneq ($(findstring $(MAKEFLAGS),w),w)
+PRINT_DIR = --no-print-directory
+else # "make -w"
+NO_SUBDIR = :
+endif
+
+# nice output definition
+# Mostly copied from kernel and git makefiles.
+
+ifndef V
+ QUIET_CC = @echo " CC" $@;
+ QUIET_LD = @echo " LD" $@;
+ QUIET_LEX = @echo " LE" $@;
+ QUIET_YACC = @echo " YA" $@;
+ QUIET_DEP = @echo " DEP" $@;
+ QUIET_GEN = @echo " GEN" $@;
+ QUIET_ASCIIDOC = @echo " ASCIIDOC" $@;
+ QUIET_XMLTO = @echo " XMLTO" $@;
+
+define install
+ @echo -n " INSTALL " `echo $(ROOTDIR)$2/$(notdir $1) | sed 's:[/]\+:/:g'` ; \
+ mkdir -p $(ROOTDIR)$2; \
+ install -m "ugo+$3" $1 $(ROOTDIR)$2; echo
+endef
+define remove
+ @echo " CLEAN " $1; $(RM) -rf $1
+endef
+else
+define remove
+ $(RM) -rf $1
+endef
+define install
+ mkdir -p $(ROOTDIR)$2; \
+ install -m "ugo+$3" $1 $(ROOTDIR)$2; echo
+endef
+endif
+
+
+.PHONY: all clean install tags cscope .FORCE-TSNIF-CFLAGS
+
+all::
+
+ifneq ($(MAKECMDGOALS),clean)
+-include deps.make
+endif
+
+# main building schema
+# Module subdir (src) are supposed to fill PROGRAMS and
+# OBJS variables, and rule to link the module. The top makefile
+# will do the rest.
+
+PROGRAMS=
+OBJS=
+
+include src/Makefile
+
+ALL_CFLAGS=-fPIC -Wall -Isrc -D_GNU_SOURCE -I$(TSNIF_INCLUDE)
+
+%.o: %.c TSNIF-CFLAGS
+ $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $<
+
+all:: $(PROGRAMS) TSNIF-CFLAGS
+
+clean::
+ $(call remove, $(OBJS) $(PROGRAMS))
+ $(call remove, deps.make)
+
+mrproper::
+ git clean -xdf
+
+
+# dependencies
+# The gcc -M depedencies generation needs to repaired to include
+# subdirectory name within the target.. at least I haven't find any
+# gcc option to do that.
+
+deps.make:
+ $(QUIET_DEP)$(RM) -f deps.make; \
+ (for obj in $(OBJS); do \
+ src=`echo $$obj | sed "s/\.o/.c/"`; \
+ $(CC) $(ALL_CFLAGS) -M -MT$$obj $$src; \
+ done) > deps.make
+
+# utilities
+tags:
+ $(QUIET_GEN)$(RM) -f tags; \
+ $(FIND) . -name '*.[hc]' -print | xargs ctags -a
+
+cscope:
+ $(QUIET_GEN)$(RM) -f cscope*; \
+ $(FIND) . -name '*.[hc]' -print > cscope.files; \
+ cscope -b -icscope.files
+
+# detect prefix and cflags changes
+TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
+ $(prefix):$(exec_prefix):$(bindir):$(libdir):$(sysconfdir) #'
+
+TSNIF-CFLAGS: .FORCE-TSNIF-CFLAGS
+ @FLAGS='$(TRACK_CFLAGS)'; \
+ if test x"$$FLAGS" != x"`cat TSNIF-CFLAGS 2>/dev/null`" ; then \
+ echo "$$FLAGS" >TSNIF-CFLAGS; \
+ fi
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..af507ed
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,20 @@
+
+AC_PREREQ(2.59)
+AC_INIT(tsnif, 0.1, olsajiri@gmail.com)
+AC_CONFIG_SRCDIR([include/autoconf.make.in])
+AC_CONFIG_HEADER([include/autoconf.h])
+
+AC_PROG_CC
+
+# Configure kernel source directory.
+AC_ARG_WITH([kinc],
+ AC_HELP_STRING([--with-kinc],
+ [set kernel directory default "./include"]),
+ [kinc=$withval],
+ [kinc=./include])
+
+AC_SUBST(CONFIG_TSNIF_KINC, "$kinc")
+AC_DEFINE(CONFIG_TSNIF_VER, "AC_PACKAGE_VERSION")
+
+AC_CONFIG_FILES([include/autoconf.make])
+AC_OUTPUT
diff --git a/include/autoconf.h.in b/include/autoconf.h.in
new file mode 100644
index 0000000..4dab769
--- /dev/null
+++ b/include/autoconf.h.in
@@ -0,0 +1,9 @@
+/* @configure_input@ */
+
+#ifndef AUTOCONF_H
+#define AUTOCONF_H
+
+/* Version defines. */
+#undef CONFIG_TSNIF_VER
+
+#endif // !AUTOCONF_H
diff --git a/include/autoconf.make.in b/include/autoconf.make.in
new file mode 100644
index 0000000..6cbfeb6
--- /dev/null
+++ b/include/autoconf.make.in
@@ -0,0 +1,19 @@
+# @configure_input@
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+bindir = @bindir@
+libdir = @libdir@
+datadir = @datadir@
+mandir = @mandir@
+datarootdir = @datarootdir@
+
+RM = rm
+FIND = find
+CC = @CC@
+CPPFLAGS = @CPPFLAGS@
+CFLAGS = @CFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+
+CONFIG_TSNIF_KINC = @CONFIG_TSNIF_KINC@
diff --git a/include/linux/tsnif.h b/include/linux/tsnif.h
new file mode 100644
index 0000000..8582c1c
--- /dev/null
+++ b/include/linux/tsnif.h
@@ -0,0 +1,62 @@
+#ifndef TSNIF_H
+#define TSNIF_H
+
+/* TODO there has to be better way */
+#ifndef __KERNEL__
+#include <netlink/attr.h>
+#endif
+
+#define TSNIF_VERSION 1
+
+/* attributes */
+enum {
+ TSNIF_ATTR_UNSPEC,
+ TSNIF_ATTR_TYPE,
+ TSNIF_ATTR_IDX,
+ TSNIF_ATTR_GROUP,
+ TSNIF_ATTR_ERR,
+ TSNIF_ATTR_DATA,
+ TSNIF_ATTR_WS,
+ TSNIF_ATTR_TIME,
+ TSNIF_ATTR_FLAGS,
+ __TSNIF_ATTR_MAX,
+};
+
+#define TSNIF_ATTR_MAX (__TSNIF_ATTR_MAX - 1)
+
+#define TSNIF_POLICY(var) \
+static struct nla_policy var[TSNIF_ATTR_MAX + 1] = { \
+ [TSNIF_ATTR_TYPE] = { .type = NLA_U32 }, \
+ [TSNIF_ATTR_IDX] = { .type = NLA_U32 }, \
+ [TSNIF_ATTR_GROUP] = { .type = NLA_U32 }, \
+ [TSNIF_ATTR_FLAGS] = { .type = NLA_U32 }, \
+ [TSNIF_ATTR_DATA] = { .type = NLA_UNSPEC}, \
+};
+
+enum {
+ TSNIF_CMD_ATTACH,
+ TSNIF_CMD_DETACH,
+ TSNIF_CMD_RELEASE,
+ TSNIF_CMD_MGROUP,
+ TSNIF_CMD_TGROUP,
+ TSNIF_CMD_DATA,
+ TSNIF_CMD_TTY_CREATE,
+ TSNIF_CMD_TTY_RELEASE,
+ __TSNIF_CMD_MAX,
+};
+
+#define TSNIF_CMD_MAX (__TSNIF_CMD_MAX - 1)
+
+enum {
+ TSNIF_FLAGS_PTY_MASTER,
+ TSNIF_FLAGS_PTY_SLAVE,
+};
+
+enum {
+ TSNIF_TYPE_TTY,
+ TSNIF_TYPE_TTYS,
+ TSNIF_TYPE_PTY,
+ TSNIF_TYPE_MAX,
+};
+
+#endif /* TSNIF_H */
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);
+}