diff options
author | Jiri Olsa <Jiri Olsa jolsa@redhat.com> | 2010-03-25 08:42:19 +0100 |
---|---|---|
committer | Jiri Olsa <Jiri Olsa jolsa@redhat.com> | 2010-03-25 08:42:19 +0100 |
commit | 24e91dc5627b181f90c800cf613614e5787e76da (patch) | |
tree | 630eb6e8b18492b5d3c3624ab94be3f3e21bc8c0 | |
download | tsnif-24e91dc5627b181f90c800cf613614e5787e76da.tar.gz tsnif-24e91dc5627b181f90c800cf613614e5787e76da.tar.xz tsnif-24e91dc5627b181f90c800cf613614e5787e76da.zip |
initial commit
-rw-r--r-- | Makefile | 115 | ||||
-rw-r--r-- | configure.ac | 20 | ||||
-rw-r--r-- | include/autoconf.h.in | 9 | ||||
-rw-r--r-- | include/autoconf.make.in | 19 | ||||
-rw-r--r-- | include/linux/tsnif.h | 62 | ||||
-rw-r--r-- | src/Makefile | 27 | ||||
-rw-r--r-- | src/fsm.c | 127 | ||||
-rw-r--r-- | src/fsm.h | 9 | ||||
-rw-r--r-- | src/intf.c | 175 | ||||
-rw-r--r-- | src/intf.h | 80 | ||||
-rw-r--r-- | src/list.h | 82 | ||||
-rw-r--r-- | src/sim-pty.c | 160 | ||||
-rw-r--r-- | src/trans-libnl.c | 195 | ||||
-rw-r--r-- | src/trans-libnl.h | 12 | ||||
-rw-r--r-- | src/trans.h | 41 | ||||
-rw-r--r-- | src/tsnif.c | 207 |
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); +} |