From f1769465b8deb4d764fa1131b3d34d2d7f238e94 Mon Sep 17 00:00:00 2001 From: jolsa@redhat.com Date: Tue, 30 Mar 2010 10:41:56 +0200 Subject: [PATCH 3/3] alive --- drivers/char/Kconfig | 9 + drivers/char/Makefile | 2 + drivers/char/tsnif.c | 857 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/char/tty_io.c | 29 ++ include/linux/tsnif.h | 63 ++++ include/linux/tty.h | 1 + 6 files changed, 961 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..a076f1c --- /dev/null +++ b/drivers/char/tsnif.c @@ -0,0 +1,857 @@ + +#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 int term_del(struct tsnif_term *term); + +static void ref_inc(atomic_t *r) +{ + smp_mb__before_atomic_inc(); + atomic_inc(r); + smp_mb__after_atomic_inc(); +} + +static int ref_dec_and_test(atomic_t *r) +{ + int ret; + + smp_mb__before_atomic_dec(); + ret = atomic_dec_and_test(r); + smp_mb__before_atomic_dec(); + + return ret; +} + +static struct tsnif_term* get_term(struct tsnif_term* term) +{ + if (term) + ref_inc(&term->refcnt); + + return term; +} + +static int put_term(struct tsnif_term *term) +{ + if (!ref_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_irq(&term_lock); + term = term_find(type, tty->index); + if (term) + get_term(term); + read_unlock_irq(&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; +} + +/* XXX explain why to use TSNIF_OK for ACK */ +#define TSNIF_RET(err) (err ? err : TSNIF_OK) + +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 TSNIF_RET(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 TSNIF_RET(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 TSNIF_RET(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..86e49fd --- /dev/null +++ b/include/linux/tsnif.h @@ -0,0 +1,63 @@ +#ifndef TSNIF_H +#define TSNIF_H + +/* TODO there has to be better way */ +#ifndef __KERNEL__ +#include +#endif + +#define TSNIF_VERSION 1 +#define TSNIF_OK 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