summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiri Olsa <Jiri Olsa jolsa@redhat.com>2010-03-25 09:16:15 +0100
committerJiri Olsa <Jiri Olsa jolsa@redhat.com>2010-03-25 09:16:15 +0100
commit47b12c907b2f5d9a054909fb9acc2cab62f0ed8c (patch)
tree615d90825b55fcd795ff257bc3ebb70c11a92c93
parent24e91dc5627b181f90c800cf613614e5787e76da (diff)
downloadtsnif-47b12c907b2f5d9a054909fb9acc2cab62f0ed8c.tar.gz
tsnif-47b12c907b2f5d9a054909fb9acc2cab62f0ed8c.tar.xz
tsnif-47b12c907b2f5d9a054909fb9acc2cab62f0ed8c.zip
kernel patch
-rw-r--r--kernel/0001-tty-notifier.patch224
-rw-r--r--kernel/0002-gennl-changes-adding-non-locking-multicast-functions.patch81
-rw-r--r--kernel/0003-alive.patch1013
-rw-r--r--kernel/README2
4 files changed, 1320 insertions, 0 deletions
diff --git a/kernel/0001-tty-notifier.patch b/kernel/0001-tty-notifier.patch
new file mode 100644
index 0000000..519cd55
--- /dev/null
+++ b/kernel/0001-tty-notifier.patch
@@ -0,0 +1,224 @@
+From 6579fc26064c146dd886ddc1d63c8469e9d8fc65 Mon Sep 17 00:00:00 2001
+From: jolsa@redhat.com <jolsa@redhat.com>
+Date: Mon, 8 Mar 2010 17:52:48 +0100
+Subject: [PATCH 1/3] tty notifier
+
+---
+ drivers/char/Kconfig | 6 +++
+ drivers/char/tty_io.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++++
+ include/linux/tty.h | 22 ++++++++++
+ 3 files changed, 134 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
+index 3141dd3..9be342e 100644
+--- a/drivers/char/Kconfig
++++ b/drivers/char/Kconfig
+@@ -1113,5 +1113,11 @@ config DEVPORT
+
+ source "drivers/s390/char/Kconfig"
+
++config TTY_NOTIFIER
++ bool "Terminal notifier"
++ depends on EXPERIMENTAL
++ default n
++ help
++ Interface to norify about terminals' data and state changes.
+ endmenu
+
+diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
+index a42c466..38f2d7b 100644
+--- a/drivers/char/tty_io.c
++++ b/drivers/char/tty_io.c
+@@ -154,6 +154,90 @@ static void release_tty(struct tty_struct *tty, int idx);
+ static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty);
+ static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty);
+
++#ifdef CONFIG_TTY_NOTIFIER
++static LIST_HEAD(tty_structs); /* linked list of tty structs */
++static ATOMIC_NOTIFIER_HEAD(tty_notifier_list);
++
++/**
++ * register_tty_notifier - register tty notifier
++ *
++ * Locking: none
++ */
++
++int register_tty_notifier(struct notifier_block *nb)
++{
++ return atomic_notifier_chain_register(&tty_notifier_list, nb);
++}
++EXPORT_SYMBOL_GPL(register_tty_notifier);
++
++/**
++ * unregister_tty_notifier - unregister tty notifier
++ *
++ * Locking: none
++ */
++
++int unregister_tty_notifier(struct notifier_block *nb)
++{
++ return atomic_notifier_chain_unregister(&tty_notifier_list, nb);
++}
++EXPORT_SYMBOL_GPL(unregister_tty_notifier);
++
++
++/**
++ * notify_tty_open - notify about tty open
++ *
++ * Calls the tty notifier chain.
++ *
++ * Locking: none
++ */
++
++static void notify_tty_create(struct tty_struct *tty)
++{
++ struct tty_notifier_param param = {
++ .tty = tty,
++ };
++
++ atomic_notifier_call_chain(&tty_notifier_list, TTY_NOTIFY_CREATE, &param);
++}
++
++/**
++ * notify_tty_close - notify about tty close
++ *
++ * Calls the tty notifier chain.
++ *
++ * Locking: none
++ */
++
++static void notify_tty_release(struct tty_struct *tty)
++{
++ struct tty_notifier_param param = {
++ .tty = tty,
++ };
++
++ atomic_notifier_call_chain(&tty_notifier_list, TTY_NOTIFY_RELEASE, &param);
++}
++
++/**
++ * notify_tty_data - notify about tty data
++ *
++ * Calls the tty notifier chain.
++ *
++ * Locking: none
++ */
++
++static void notify_tty_data(struct tty_struct *tty, unsigned char *buf, size_t size)
++{
++ struct tty_notifier_param param = {
++ .tty = tty,
++ .buf = buf,
++ .size = size
++ };
++
++ atomic_notifier_call_chain(&tty_notifier_list, TTY_NOTIFY_DATA, &param);
++}
++
++#endif
++
+ /**
+ * alloc_tty_struct - allocate a tty object
+ *
+@@ -981,6 +1065,11 @@ static inline ssize_t do_tty_write(
+ ret = -EFAULT;
+ if (copy_from_user(tty->write_buf, buf, size))
+ break;
++
++#ifdef CONFIG_TTY_NOTIFIER
++ notify_tty_data(tty, tty->write_buf, size);
++#endif
++
+ ret = write(tty, file, tty->write_buf, size);
+ if (ret <= 0)
+ break;
+@@ -1352,6 +1441,11 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
+ retval = tty_ldisc_setup(tty, tty->link);
+ if (retval)
+ goto release_mem_out;
++
++#ifdef CONFIG_TTY_NOTIFIER
++ list_add(&tty->list, &tty_structs);
++ notify_tty_create(tty);
++#endif
+ return tty;
+
+ fail_no_mem:
+@@ -1474,6 +1568,10 @@ static void release_tty(struct tty_struct *tty, int idx)
+ /* This should always be true but check for the moment */
+ WARN_ON(tty->index != idx);
+
++#ifdef CONFIG_TTY_NOTIFIER
++ list_del(&tty->list);
++#endif
++
+ if (tty->link)
+ tty_kref_put(tty->link);
+ tty_kref_put(tty);
+@@ -1703,6 +1801,11 @@ int tty_release(struct inode *inode, struct file *filp)
+ #ifdef TTY_DEBUG_HANGUP
+ printk(KERN_DEBUG "freeing tty structure...");
+ #endif
++
++#ifdef CONFIG_TTY_NOTIFIER
++ notify_tty_release(tty);
++#endif
++
+ /*
+ * Ask the line discipline code to release its structures
+ */
+@@ -2768,6 +2871,9 @@ void initialize_tty_struct(struct tty_struct *tty,
+ spin_lock_init(&tty->read_lock);
+ spin_lock_init(&tty->ctrl_lock);
+ INIT_LIST_HEAD(&tty->tty_files);
++#ifdef CONFIG_TTY_NOTIFIER
++ INIT_LIST_HEAD(&tty->list);
++#endif
+ INIT_WORK(&tty->SAK_work, do_SAK_work);
+
+ tty->driver = driver;
+diff --git a/include/linux/tty.h b/include/linux/tty.h
+index 4409967..7d57719 100644
+--- a/include/linux/tty.h
++++ b/include/linux/tty.h
+@@ -13,6 +13,7 @@
+ #include <linux/tty_driver.h>
+ #include <linux/tty_ldisc.h>
+ #include <linux/mutex.h>
++#include <linux/notifier.h>
+
+ #include <asm/system.h>
+
+@@ -324,8 +325,29 @@ struct tty_struct {
+ /* If the tty has a pending do_SAK, queue it here - akpm */
+ struct work_struct SAK_work;
+ struct tty_port *port;
++#ifdef CONFIG_TTY_NOTIFIER
++ struct list_head list;
++#endif
++};
++
++
++#ifdef CONFIG_TTY_NOTIFIER
++
++#define TTY_NOTIFY_DATA 1
++#define TTY_NOTIFY_CREATE 2
++#define TTY_NOTIFY_RELEASE 3
++
++struct tty_notifier_param {
++ struct tty_struct *tty;
++ unsigned char *buf;
++ size_t size;
+ };
+
++extern int register_tty_notifier(struct notifier_block *nb);
++extern int unregister_tty_notifier(struct notifier_block *nb);
++
++#endif /* CONFIG_TTY_NOTIFIER */
++
+ /* tty magic number */
+ #define TTY_MAGIC 0x5401
+
+--
+1.6.6.1
+
diff --git a/kernel/0002-gennl-changes-adding-non-locking-multicast-functions.patch b/kernel/0002-gennl-changes-adding-non-locking-multicast-functions.patch
new file mode 100644
index 0000000..dfa33d1
--- /dev/null
+++ b/kernel/0002-gennl-changes-adding-non-locking-multicast-functions.patch
@@ -0,0 +1,81 @@
+From e3c4d76ad2cf0bb12d2fe7949716ace17929c2c7 Mon Sep 17 00:00:00 2001
+From: jolsa@redhat.com <jolsa@redhat.com>
+Date: Thu, 25 Mar 2010 08:59:04 +0100
+Subject: [PATCH 2/3] gennl changes - adding non locking multicast functions
+
+---
+ include/net/genetlink.h | 5 +++++
+ net/netlink/genetlink.c | 20 +++++++++++++++-----
+ 2 files changed, 20 insertions(+), 5 deletions(-)
+
+diff --git a/include/net/genetlink.h b/include/net/genetlink.h
+index eb551ba..e06a026 100644
+--- a/include/net/genetlink.h
++++ b/include/net/genetlink.h
+@@ -119,8 +119,13 @@ extern int genl_register_ops(struct genl_family *, struct genl_ops *ops);
+ extern int genl_unregister_ops(struct genl_family *, struct genl_ops *ops);
+ extern int genl_register_mc_group(struct genl_family *family,
+ struct genl_multicast_group *grp);
++extern int __genl_register_mc_group(struct genl_family *family,
++ struct genl_multicast_group *grp);
+ extern void genl_unregister_mc_group(struct genl_family *family,
+ struct genl_multicast_group *grp);
++extern void __genl_unregister_mc_group(struct genl_family *family,
++ struct genl_multicast_group *grp);
++
+
+ /**
+ * genlmsg_put - Add generic netlink header to netlink message
+diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c
+index a4b6e14..5d9d158 100644
+--- a/net/netlink/genetlink.c
++++ b/net/netlink/genetlink.c
+@@ -123,7 +123,7 @@ static struct genl_multicast_group notify_grp;
+ * @family: The generic netlink family the group shall be registered for.
+ * @grp: The group to register, must have a name.
+ */
+-int genl_register_mc_group(struct genl_family *family,
++int __genl_register_mc_group(struct genl_family *family,
+ struct genl_multicast_group *grp)
+ {
+ int id;
+@@ -132,8 +132,6 @@ int genl_register_mc_group(struct genl_family *family,
+
+ BUG_ON(grp->name[0] == '\0');
+
+- genl_lock();
+-
+ /* special-case our own group */
+ if (grp == &notify_grp)
+ id = GENL_ID_CTRL;
+@@ -201,13 +199,25 @@ int genl_register_mc_group(struct genl_family *family,
+
+ genl_ctrl_event(CTRL_CMD_NEWMCAST_GRP, grp);
+ out:
++ return err;
++}
++EXPORT_SYMBOL(__genl_register_mc_group);
++
++int genl_register_mc_group(struct genl_family *family,
++ struct genl_multicast_group *grp)
++{
++ int err;
++
++ genl_lock();
++ err = __genl_register_mc_group(family, grp);
+ genl_unlock();
++
+ return err;
+ }
+ EXPORT_SYMBOL(genl_register_mc_group);
+
+-static void __genl_unregister_mc_group(struct genl_family *family,
+- struct genl_multicast_group *grp)
++void __genl_unregister_mc_group(struct genl_family *family,
++ struct genl_multicast_group *grp)
+ {
+ struct net *net;
+ BUG_ON(grp->family != family);
+--
+1.6.6.1
+
diff --git a/kernel/0003-alive.patch b/kernel/0003-alive.patch
new file mode 100644
index 0000000..e35a005
--- /dev/null
+++ b/kernel/0003-alive.patch
@@ -0,0 +1,1013 @@
+From 602cf474beb50b6c367b3ebf02685223fb165b51 Mon Sep 17 00:00:00 2001
+From: jolsa@redhat.com <jolsa@redhat.com>
+Date: Thu, 25 Mar 2010 09:00:31 +0100
+Subject: [PATCH 3/3] alive
+
+---
+ drivers/char/Kconfig | 9 +
+ drivers/char/Makefile | 2 +
+ drivers/char/tsnif.c | 836 +++++++++++++++++++++++++++++++++++++++++++++++++
+ drivers/char/tty_io.c | 29 ++
+ include/linux/tsnif.h | 62 ++++
+ include/linux/tty.h | 1 +
+ 6 files changed, 939 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/char/tsnif.c
+ create mode 100644 include/linux/tsnif.h
+
+diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
+index 9be342e..5255204 100644
+--- a/drivers/char/Kconfig
++++ b/drivers/char/Kconfig
+@@ -1119,5 +1119,14 @@ config TTY_NOTIFIER
+ default n
+ help
+ Interface to norify about terminals' data and state changes.
++
++config TSNIF
++ tristate "Terminal sniffer interface"
++ depends on EXPERIMENTAL
++ select TTY_NOTIFIER
++ default n
++ help
++ The tsnif project interface.
++
+ endmenu
+
+diff --git a/drivers/char/Makefile b/drivers/char/Makefile
+index f957edf..537c771 100644
+--- a/drivers/char/Makefile
++++ b/drivers/char/Makefile
+@@ -111,6 +111,8 @@ obj-$(CONFIG_PS3_FLASH) += ps3flash.o
+ obj-$(CONFIG_JS_RTC) += js-rtc.o
+ js-rtc-y = rtc.o
+
++obj-$(CONFIG_TSNIF) += tsnif.o
++
+ # Files generated that shall be removed upon make clean
+ clean-files := consolemap_deftbl.c defkeymap.c
+
+diff --git a/drivers/char/tsnif.c b/drivers/char/tsnif.c
+new file mode 100644
+index 0000000..ea31a35
+--- /dev/null
++++ b/drivers/char/tsnif.c
+@@ -0,0 +1,836 @@
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <net/genetlink.h>
++#include <linux/workqueue.h>
++#include <linux/tty.h>
++#include <linux/list.h>
++#include <linux/idr.h>
++#include <linux/time.h>
++#include <linux/tsnif.h>
++#include <linux/sched.h>
++
++#define TSNIF_DEBUG(fmt, args...) \
++do { \
++ if (tsnif_debug) { \
++ char lpbuf[256]; \
++ snprintf(lpbuf, sizeof(lpbuf)-1, "[%3d:%s:%05d] %s", \
++ smp_processor_id(), \
++ __FUNCTION__, \
++ __LINE__, \
++ fmt); \
++ printk(lpbuf, ## args); \
++ } \
++} while(0)
++
++int tsnif_debug = 0;
++module_param_named(debug, tsnif_debug, int, 0444);
++MODULE_PARM_DESC(debug, "debug output");
++
++static DEFINE_RWLOCK(term_lock);
++
++struct tsnif_term {
++ int type;
++ int idx;
++ struct genl_multicast_group group;
++ atomic_t refcnt;
++ struct hlist_node node;
++};
++
++struct tsnif_work {
++ struct tsnif_term *term;
++ struct tsnif_work *next;
++ int flags;
++ int len;
++ char *data;
++ struct winsize ws;
++ struct timespec time;
++};
++
++DEFINE_PER_CPU(struct tsnif_work*, worker_head);
++DEFINE_PER_CPU(struct tsnif_work*, worker_tail);
++
++static struct genl_family gnl_family = {
++ .id = GENL_ID_GENERATE,
++ .name = "tsnif",
++ .version = TSNIF_VERSION,
++ .maxattr = TSNIF_ATTR_MAX,
++ .netnsok = true,
++};
++
++static struct genl_multicast_group gnl_ctrl_group = {
++ .name = "tsnif-ctrl",
++};
++
++static struct tsnif_term* get_term(struct tsnif_term* term)
++{
++ if (term)
++ atomic_inc(&term->refcnt);
++ return term;
++}
++
++static int term_del(struct tsnif_term *term);
++
++static int put_term(struct tsnif_term *term)
++{
++ if (!atomic_dec_and_test(&term->refcnt)) {
++ return 1;
++ }
++
++ __genl_unregister_mc_group(&gnl_family, &term->group);
++
++ term_del(term);
++ kfree(term);
++ return 0;
++}
++
++static struct hlist_head *term_hash;
++
++struct tsnif_pid {
++ int pid;
++ struct list_head terms;
++ struct list_head list;
++};
++
++struct tsnif_pid_term {
++ struct tsnif_term *term;
++ struct list_head list;
++};
++
++static LIST_HEAD(pids);
++
++static int get_type(struct tty_struct *tty)
++{
++ switch(tty->driver->type) {
++ case TTY_DRIVER_TYPE_SYSTEM:
++ return TSNIF_TYPE_TTY;
++ case TTY_DRIVER_TYPE_SERIAL:
++ return TSNIF_TYPE_TTYS;
++ case TTY_DRIVER_TYPE_PTY:
++ return TSNIF_TYPE_PTY;
++ default:
++ return -1;
++ }
++}
++
++static int get_flags(struct tty_struct *tty)
++{
++ struct tty_driver *driver = tty->driver;
++
++ if (driver->subtype == PTY_TYPE_MASTER)
++ return TSNIF_FLAGS_PTY_MASTER;
++
++ if (driver->subtype == PTY_TYPE_SLAVE)
++ return TSNIF_FLAGS_PTY_SLAVE;
++
++ return 0;
++}
++
++static struct sk_buff *make_msg(int size, int cmd, int pid, int seq)
++{
++ struct sk_buff *skb;
++ void *p;
++
++ skb = genlmsg_new(size, GFP_KERNEL);
++ if (!skb)
++ return NULL;
++
++ p = genlmsg_put(skb, pid, seq, &gnl_family, 0, cmd);
++ if (!p) {
++ nlmsg_free(skb);
++ return NULL;
++ }
++
++ return skb;
++}
++
++static int send_msg(struct sk_buff *skb, int pid)
++{
++ void *hdr = genlmsg_data(NLMSG_DATA(skb->data));
++
++ if (genlmsg_end(skb, hdr) < 0)
++ goto out;
++
++ TSNIF_DEBUG("pid %d\n", pid);
++ return genlmsg_unicast(&init_net, skb, pid);
++
++out:
++ nlmsg_free(skb);
++ return -ENOBUFS;
++}
++
++static int send_group(struct genl_info *info, int group, int type, int idx)
++{
++ struct sk_buff *skb;
++
++ skb = make_msg(NLMSG_GOODSIZE, TSNIF_CMD_TGROUP,
++ info->snd_pid, info->snd_seq);
++ if (!skb)
++ return -ENOMEM;
++
++ NLA_PUT_U32(skb, TSNIF_ATTR_TYPE, type);
++ NLA_PUT_U32(skb, TSNIF_ATTR_IDX, idx);
++ NLA_PUT_U32(skb, TSNIF_ATTR_GROUP, group);
++
++ TSNIF_DEBUG("type %d, idx %d, group %d\n", type, idx, group);
++ return send_msg(skb, info->snd_pid);
++
++nla_put_failure:
++ nlmsg_free(skb);
++ return -EMSGSIZE;
++}
++
++static int send_notify(int cmd, int type, int idx)
++{
++ struct sk_buff *skb;
++ void *hdr;
++
++ skb = make_msg(NLMSG_GOODSIZE, cmd, 0, 0);
++ if (!skb)
++ return -ENOMEM;
++
++ NLA_PUT_U32(skb, TSNIF_ATTR_TYPE, type);
++ NLA_PUT_U32(skb, TSNIF_ATTR_IDX, idx);
++
++ hdr = genlmsg_data(NLMSG_DATA(skb->data));
++ if (genlmsg_end(skb, hdr) < 0)
++ goto nla_put_failure;
++
++ return genlmsg_multicast_allns(skb, 0, gnl_ctrl_group.id, GFP_KERNEL);
++
++nla_put_failure:
++ nlmsg_free(skb);
++ return -EMSGSIZE;
++}
++
++static int send_data(struct tsnif_work *wd)
++{
++ struct sk_buff *skb;
++ struct tsnif_term *term = wd->term;
++ struct nlattr *ret;
++ void *hdr;
++
++ /* TODO seq storing on tsnif_term level */
++ skb = make_msg(NLMSG_GOODSIZE, TSNIF_CMD_DATA, 0, 0);
++
++ NLA_PUT_U32(skb, TSNIF_ATTR_TYPE, term->type);
++ NLA_PUT_U32(skb, TSNIF_ATTR_IDX, term->idx);
++ NLA_PUT_U32(skb, TSNIF_ATTR_FLAGS, wd->flags);
++
++ /* terminal size */
++ ret = nla_reserve(skb, TSNIF_ATTR_WS, sizeof(struct winsize));
++ if (!ret)
++ goto nla_put_failure;
++
++ memcpy(nla_data(ret), &wd->ws, sizeof(struct winsize));
++
++ /* time */
++ ret = nla_reserve(skb, TSNIF_ATTR_TIME, sizeof(struct timespec));
++ if (!ret)
++ goto nla_put_failure;
++
++ memcpy(nla_data(ret), &wd->time, sizeof(struct timespec));
++
++ /* data */
++ ret = nla_reserve(skb, TSNIF_ATTR_DATA, wd->len);
++ if (!ret)
++ goto nla_put_failure;
++
++ memcpy(nla_data(ret), wd->data, wd->len);
++
++ hdr = genlmsg_data(NLMSG_DATA(skb->data));
++ if (genlmsg_end(skb, hdr) < 0)
++ goto nla_put_failure;
++
++ TSNIF_DEBUG("sending data to group %d\n", term->group.id);
++ return genlmsg_multicast_allns(skb, 0, term->group.id, GFP_KERNEL);
++
++nla_put_failure:
++ nlmsg_free(skb);
++ return -EMSGSIZE;
++}
++
++static void worker(struct work_struct *work)
++{
++ struct tsnif_work *wd_head;
++ unsigned long flags;
++
++ local_irq_save(flags);
++
++ wd_head = __get_cpu_var(worker_head);
++ __get_cpu_var(worker_head) = NULL;
++
++ local_irq_restore(flags);
++
++ smp_mb();
++
++ for(;wd_head; wd_head = wd_head->next) {
++
++ struct tsnif_work *wd = wd_head;
++ struct tsnif_term *term = wd->term;
++
++ TSNIF_DEBUG("got work for type %d, idx %d\n",
++ term->type, term->idx);
++
++ send_data(wd);
++
++ kfree(wd->data);
++ put_term(term);
++ kfree(wd);
++ }
++}
++
++static DECLARE_WORK(work, worker);
++
++static int schedule_worker(struct tty_struct *tty, struct tsnif_term *term, char *buf, int len)
++{
++ struct tsnif_work *wd;
++ unsigned long flags;
++
++ wd = kzalloc(sizeof(*wd), GFP_ATOMIC);
++ if (!wd)
++ return 1;
++
++ wd->data = kmalloc(len, GFP_ATOMIC);
++ if (!wd->data) {
++ kfree(wd);
++ return 1;
++ }
++
++ wd->term = term;
++ wd->flags = get_flags(tty);
++ wd->len = len;
++ wd->time = current_kernel_time();
++ wd->ws = tty->winsize;
++ memcpy(wd->data, buf, len);
++
++ smp_mb();
++
++ local_irq_save(flags);
++ if (!__get_cpu_var(worker_head))
++ __get_cpu_var(worker_head) = wd;
++ else
++ __get_cpu_var(worker_tail)->next = wd;
++
++ __get_cpu_var(worker_tail) = wd;
++ local_irq_restore(flags);
++
++ TSNIF_DEBUG("scheduling work for type %d, idx %d\n",
++ term->type, term->idx);
++
++ schedule_work(&work);
++ return 0;
++}
++
++static struct tsnif_pid_term *pid_term_find(struct tsnif_pid *pid, struct tsnif_term *term)
++{
++ struct tsnif_pid_term *pt;
++
++ list_for_each_entry(pt, &pid->terms, list)
++ if (pt->term == term)
++ return pt;
++ return NULL;
++}
++
++static int pid_term_add(struct tsnif_pid *pid, struct tsnif_term *term)
++{
++ struct tsnif_pid_term *pt;
++
++ pt = kmalloc(sizeof(*pt), GFP_KERNEL);
++ if (!pt)
++ return -ENOMEM;
++
++ INIT_LIST_HEAD(&pt->list);
++ pt->term = term;
++ list_add(&pt->list, &pid->terms);
++ return 0;
++}
++
++static int pid_term_del(struct tsnif_pid *pid, struct tsnif_term *term)
++{
++ struct tsnif_pid_term *pt;
++
++ if (!pid)
++ return -EINVAL;
++
++ list_for_each_entry(pt, &pid->terms, list)
++ if (pt->term == term) {
++ list_del(&pt->list);
++ kfree(pt);
++ return 0;
++ }
++
++ return -ENOENT;
++}
++
++static struct tsnif_pid* pid_find(int pid)
++{
++ struct tsnif_pid *p;
++
++ list_for_each_entry(p, &pids, list) {
++ if (p->pid == pid)
++ return p;
++ }
++
++ return NULL;
++}
++
++static struct tsnif_pid* pid_add(int pid)
++{
++ struct tsnif_pid *p;
++
++ p = pid_find(pid);
++ if (p)
++ return p;
++
++ p = kmalloc(sizeof(*p), GFP_KERNEL);
++ if (!p)
++ return NULL;
++
++ p->pid = pid;
++ INIT_LIST_HEAD(&p->terms);
++ list_add(&p->list, &pids);
++
++ return p;
++};
++
++static int pid_del(int pid)
++{
++ struct tsnif_pid* p;
++ struct tsnif_pid_term *pt, *pth;
++
++ p = pid_find(pid);
++ if (!p)
++ return -ENOENT;
++
++ list_for_each_entry_safe(pt, pth, &p->terms, list) {
++ put_term(pt->term);
++ kfree(pt);
++ }
++
++ list_del(&p->list);
++ return 0;
++}
++
++#define TSNIF_HASH_SIZE 32
++static int term_hash_fn(int type, int idx)
++{
++ return (type + idx) % TSNIF_HASH_SIZE;
++}
++
++static int term_add(struct tsnif_term *term)
++{
++ struct hlist_head *head;
++
++ TSNIF_DEBUG("adding type %d, idx %d\n",
++ term->type, term->idx);
++
++ head = &term_hash[term_hash_fn(term->type, term->idx)];
++ hlist_add_head(&term->node, head);
++ return 0;
++}
++
++static int term_del(struct tsnif_term *term)
++{
++ TSNIF_DEBUG("removing type %d, idx %d\n",
++ term->type, term->idx);
++
++ hlist_del(&term->node);
++ return 0;
++}
++
++static struct tsnif_term* term_find(int type, int idx)
++{
++ struct hlist_head *head;
++ struct hlist_node *node;
++ struct tsnif_term *term;
++
++ head = &term_hash[term_hash_fn(type, idx)];
++ hlist_for_each_entry(term, node, head, node)
++ if (term->idx == idx)
++ return term;
++
++ return NULL;
++}
++
++static bool check_term(int type, int idx)
++{
++ int tt = -1;
++
++ if (type == TSNIF_TYPE_TTY)
++ tt = TTY_DRIVER_TYPE_SYSTEM;
++ else if (type == TSNIF_TYPE_TTYS)
++ tt = TTY_DRIVER_TYPE_SERIAL;
++ else if (type == TSNIF_TYPE_PTY)
++ tt = TTY_DRIVER_TYPE_PTY;
++
++ return tty_is_active(tt, idx);
++}
++
++static int term_new(struct tsnif_term **term, int type, int idx)
++{
++ struct tsnif_term *t;
++ int err;
++
++ TSNIF_DEBUG("type %d, idx %d\n", type, idx);
++
++ t = kmalloc(sizeof(*t), GFP_KERNEL);
++ if (!t)
++ return -ENOMEM;
++
++ memset(t, 0, sizeof(*t));
++ t->idx = idx;
++ t->type = type;
++ sprintf(t->group.name, "%5d-%5d\n", type, idx);
++
++ err = __genl_register_mc_group(&gnl_family, &t->group);
++ if (err) {
++ kfree(t);
++ return err;
++ }
++
++ TSNIF_DEBUG("group %d\n", t->group.id);
++
++ *term = t;
++ return 0;
++}
++
++static int attach(int type, int idx)
++{
++ struct tsnif_term *term;
++ struct tsnif_pid *p;
++ int pid = current->pid;
++ int err = -ENOMEM;
++
++ TSNIF_DEBUG("type %d, idx %d\n", type, idx);
++
++ p = pid_add(pid);
++ if (!p)
++ goto out;
++
++ term = term_find(type, idx);
++ if (!term) {
++ err = term_new(&term, type, idx);
++ if (err)
++ goto out;
++
++ term_add(term);
++
++ } else if (pid_term_find(p, term)) {
++ /*
++ * No need to call pid_del. It will be called via
++ * netlink notifier once the socket is down.
++ */
++ err = -EAGAIN;
++ goto out;
++ }
++
++ get_term(term);
++
++ err = pid_term_add(p, term);
++ if (err)
++ goto out;
++
++ err = 0;
++
++out:
++ TSNIF_DEBUG("err = %d\n", err);
++ return err;
++}
++
++static int detach(int type, int idx)
++{
++ struct tsnif_term *term;
++ int pid = current->pid;
++ int err = -EINVAL;
++
++ TSNIF_DEBUG("type %d, idx %d\n", type, idx);
++
++ write_lock(&term_lock);
++
++ term = term_find(type, idx);
++ if (!term)
++ goto out;
++
++ if (pid_term_del(pid_find(pid), term))
++ goto out;
++
++ put_term(term);
++out:
++ write_unlock(&term_lock);
++ return err;
++}
++
++static int tty_write(struct tty_struct *tty, unsigned char *buf, size_t size)
++{
++ struct tsnif_term *term;
++ int type = get_type(tty);
++
++ read_lock(&term_lock);
++ term = term_find(type, tty->index);
++ if (term)
++ get_term(term);
++ read_unlock(&term_lock);
++
++ if (!term)
++ return 1;
++
++ if (schedule_worker(tty, term, buf, size))
++ printk("failed\n");
++
++ return 0;
++}
++
++static int tty_create(struct tty_struct *tty)
++{
++ return send_notify(TSNIF_CMD_TTY_CREATE,
++ get_type(tty), tty->index);
++}
++
++static int tty_rel(struct tty_struct *tty)
++{
++ return send_notify(TSNIF_CMD_TTY_RELEASE,
++ get_type(tty), tty->index);
++}
++
++static int tty_notifier(struct notifier_block *nb, unsigned long val,
++ void *data)
++{
++ struct tty_notifier_param *param = (struct tty_notifier_param *) data;
++ struct tty_struct *tty = param->tty;
++
++ switch(val) {
++ case TTY_NOTIFY_DATA:
++ tty_write(tty, param->buf, param->size);
++ break;
++ case TTY_NOTIFY_CREATE:
++ tty_create(tty);
++ break;
++ case TTY_NOTIFY_RELEASE:
++ tty_rel(tty);
++ break;
++ }
++
++ return NOTIFY_OK;
++}
++
++static struct notifier_block tty_nb = {
++ .notifier_call = tty_notifier,
++};
++
++static int netlink_notifier(struct notifier_block *nb, unsigned long val,
++ void *data)
++{
++ struct netlink_notify *n = data;
++
++ if (n->protocol != NETLINK_GENERIC)
++ return NOTIFY_DONE;
++
++ TSNIF_DEBUG("pid %d\n", n->pid);
++
++ write_lock(&term_lock);
++ pid_del(n->pid);
++ write_unlock(&term_lock);
++
++ return NOTIFY_DONE;
++}
++
++static struct notifier_block netlink_nb = {
++ .notifier_call = netlink_notifier,
++};
++
++static int hash_init(void)
++{
++ int i;
++
++ term_hash = kmalloc(TSNIF_HASH_SIZE * sizeof(struct hlist_head),
++ GFP_KERNEL);
++ if (!term_hash) {
++ printk(KERN_ERR "tsnif - failed to allocate hash!\n");
++ return -ENOMEM;
++ }
++
++ for(i = 0; i < TSNIF_HASH_SIZE; i++)
++ INIT_HLIST_HEAD(&term_hash[i]);
++
++ return 0;
++}
++
++static void hash_release(void)
++{
++ kfree(term_hash);
++}
++
++static u32 get_type_idx(struct genl_info *info, u32 *type, u32 *idx)
++{
++ struct nlattr *attr;
++
++ attr = info->attrs[TSNIF_ATTR_TYPE];
++ if (!attr)
++ return -EINVAL;
++
++ attr = info->attrs[TSNIF_ATTR_IDX];
++ if (!attr)
++ return -EINVAL;
++
++ *type = nla_get_u32(info->attrs[TSNIF_ATTR_TYPE]);
++ *idx = nla_get_u32(info->attrs[TSNIF_ATTR_IDX]);
++ return 0;
++}
++
++static int cmd_attach(struct sk_buff *skb, struct genl_info *info)
++{
++ struct tsnif_term* term;
++ int err;
++ u32 type, idx;
++
++ err = get_type_idx(info, &type, &idx);
++ if (err)
++ return err;
++
++ if (!check_term(type, idx)) {
++ TSNIF_DEBUG("cannot find terminal type %d, idx %d\n",
++ type, idx);
++ return -ENOENT;
++ }
++
++ TSNIF_DEBUG("got live terminal type %d, idx %d\n", type, idx);
++
++ write_lock(&term_lock);
++
++ err = attach(type, idx);
++ if (err)
++ goto out;
++
++ term = term_find(type, idx);
++ if (!term) {
++ err = -ENOENT;
++ goto out;
++ }
++
++ err = send_group(info, term->group.id, type, idx);
++
++out:
++ write_unlock(&term_lock);
++ return err;
++}
++
++static int cmd_detach(struct sk_buff *skb, struct genl_info *info)
++{
++ int err;
++ u32 type, idx;
++
++ err = get_type_idx(info, &type, &idx);
++ if (err)
++ return err;
++
++ return detach(type, idx);
++}
++
++static int cmd_mgroup(struct sk_buff *skb, struct genl_info *info)
++{
++ struct sk_buff *skb_answer;
++
++ skb_answer = make_msg(NLMSG_GOODSIZE, TSNIF_CMD_MGROUP,
++ info->snd_pid, info->snd_seq);
++
++ if (!skb)
++ return -ENOMEM;
++
++ NLA_PUT_U32(skb_answer, TSNIF_ATTR_GROUP, gnl_ctrl_group.id);
++
++ TSNIF_DEBUG("sending control group ID %d\n", gnl_ctrl_group.id);
++ return send_msg(skb_answer, info->snd_pid);
++
++nla_put_failure:
++ nlmsg_free(skb);
++ return -EMSGSIZE;
++}
++
++TSNIF_POLICY(gnl_policy);
++
++static struct genl_ops gnl_ops_attach = {
++ .cmd = TSNIF_CMD_ATTACH,
++ .doit = cmd_attach,
++ .policy = gnl_policy,
++};
++
++static struct genl_ops gnl_ops_detach = {
++ .cmd = TSNIF_CMD_DETACH,
++ .doit = cmd_detach,
++ .policy = gnl_policy,
++};
++
++static struct genl_ops gnl_ops_mgroup = {
++ .cmd = TSNIF_CMD_MGROUP,
++ .doit = cmd_mgroup,
++ .policy = gnl_policy,
++};
++
++int __init tsnif_init(void)
++{
++ int err;
++
++ err = hash_init();
++ if (err < 0)
++ return err;
++
++ err = genl_register_family(&gnl_family);
++ if (err < 0)
++ goto release_hash;
++
++
++ err = genl_register_mc_group(&gnl_family, &gnl_ctrl_group);
++ if (err)
++ goto release_family;
++
++ err = genl_register_ops(&gnl_family, &gnl_ops_attach);
++ if (err < 0)
++ goto release_gnl;
++
++ err = genl_register_ops(&gnl_family, &gnl_ops_detach);
++ if (err < 0)
++ goto release_gnl;
++
++ err = genl_register_ops(&gnl_family, &gnl_ops_mgroup);
++ if (err < 0)
++ goto release_gnl;
++
++ register_tty_notifier(&tty_nb);
++ netlink_register_notifier(&netlink_nb);
++
++ printk("tsnif interface loaded\n");
++ return 0;
++
++release_gnl:
++ genl_unregister_mc_group(&gnl_family, &gnl_ctrl_group);
++ genl_unregister_ops(&gnl_family, &gnl_ops_attach);
++ genl_unregister_ops(&gnl_family, &gnl_ops_detach);
++
++release_family:
++ genl_unregister_family(&gnl_family);
++
++release_hash:
++ hash_release();
++ return err;
++}
++
++void tsnif_exit(void)
++{
++ unregister_tty_notifier(&tty_nb);
++ netlink_unregister_notifier(&netlink_nb);
++
++ genl_unregister_mc_group(&gnl_family, &gnl_ctrl_group);
++ genl_unregister_ops(&gnl_family, &gnl_ops_attach);
++ genl_unregister_ops(&gnl_family, &gnl_ops_detach);
++ genl_unregister_ops(&gnl_family, &gnl_ops_mgroup);
++ genl_unregister_family(&gnl_family);
++
++ hash_release();
++ printk("tsnif interface unloaded\n");
++}
++
++module_init(tsnif_init);
++module_exit(tsnif_exit);
++
++MODULE_LICENSE("GPL");
+diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
+index 38f2d7b..11924dc 100644
+--- a/drivers/char/tty_io.c
++++ b/drivers/char/tty_io.c
+@@ -159,6 +159,35 @@ static LIST_HEAD(tty_structs); /* linked list of tty structs */
+ static ATOMIC_NOTIFIER_HEAD(tty_notifier_list);
+
+ /**
++ * tty_is_active - checks wether the tty is active
++ * @type: tty driver type
++ * @line: tty line
++ *
++ * Returns true, if there's active tty for given type and line.
++ */
++bool tty_is_active(int type, int line)
++{
++ struct tty_struct *tty;
++ bool found = false;
++
++ mutex_lock(&tty_mutex);
++ list_for_each_entry(tty, &tty_structs, list) {
++ struct tty_driver *driver = tty->driver;
++
++ if (driver->type != type)
++ continue;
++
++ if (line == tty->index) {
++ found = true;
++ break;
++ }
++ }
++ mutex_unlock(&tty_mutex);
++ return found;
++}
++EXPORT_SYMBOL_GPL(tty_is_active);
++
++/**
+ * register_tty_notifier - register tty notifier
+ *
+ * Locking: none
+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/include/linux/tty.h b/include/linux/tty.h
+index 7d57719..113d155 100644
+--- a/include/linux/tty.h
++++ b/include/linux/tty.h
+@@ -345,6 +345,7 @@ struct tty_notifier_param {
+
+ extern int register_tty_notifier(struct notifier_block *nb);
+ extern int unregister_tty_notifier(struct notifier_block *nb);
++extern bool tty_is_active(int type, int line);
+
+ #endif /* CONFIG_TTY_NOTIFIER */
+
+--
+1.6.6.1
+
diff --git a/kernel/README b/kernel/README
new file mode 100644
index 0000000..a9918f3
--- /dev/null
+++ b/kernel/README
@@ -0,0 +1,2 @@
+kernel patches over the tip tree
+git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip.git