From 47b12c907b2f5d9a054909fb9acc2cab62f0ed8c Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 25 Mar 2010 09:16:15 +0100 Subject: kernel patch --- kernel/0001-tty-notifier.patch | 224 +++++ ...es-adding-non-locking-multicast-functions.patch | 81 ++ kernel/0003-alive.patch | 1013 ++++++++++++++++++++ kernel/README | 2 + 4 files changed, 1320 insertions(+) create mode 100644 kernel/0001-tty-notifier.patch create mode 100644 kernel/0002-gennl-changes-adding-non-locking-multicast-functions.patch create mode 100644 kernel/0003-alive.patch create mode 100644 kernel/README 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 +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, ¶m); ++} ++ ++/** ++ * 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, ¶m); ++} ++ ++/** ++ * 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, ¶m); ++} ++ ++#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 + #include + #include ++#include + + #include + +@@ -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 +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 == ¬ify_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 +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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#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 -- cgit