1/*
2 * proc_tty.c -- handles /proc/tty
3 *
4 * Copyright 1997, Theodore Ts'o
5 */
6
7#include <asm/uaccess.h>
8
9#include <linux/init.h>
10#include <linux/errno.h>
11#include <linux/time.h>
12#include <linux/proc_fs.h>
13#include <linux/stat.h>
14#include <linux/tty.h>
15#include <linux/seq_file.h>
16#include <linux/bitops.h>
17
18static int tty_ldiscs_read_proc(char *page, char **start, off_t off,
19				int count, int *eof, void *data);
20
21/*
22 * The /proc/tty directory inodes...
23 */
24static struct proc_dir_entry *proc_tty_ldisc, *proc_tty_driver;
25
26/*
27 * This is the handler for /proc/tty/drivers
28 */
29static void show_tty_range(struct seq_file *m, struct tty_driver *p,
30	dev_t from, int num)
31{
32	seq_printf(m, "%-20s ", p->driver_name ? p->driver_name : "unknown");
33	seq_printf(m, "/dev/%-8s ", p->name);
34	if (p->num > 1) {
35		seq_printf(m, "%3d %d-%d ", MAJOR(from), MINOR(from),
36			MINOR(from) + num - 1);
37	} else {
38		seq_printf(m, "%3d %7d ", MAJOR(from), MINOR(from));
39	}
40	switch (p->type) {
41	case TTY_DRIVER_TYPE_SYSTEM:
42		seq_printf(m, "system");
43		if (p->subtype == SYSTEM_TYPE_TTY)
44			seq_printf(m, ":/dev/tty");
45		else if (p->subtype == SYSTEM_TYPE_SYSCONS)
46			seq_printf(m, ":console");
47		else if (p->subtype == SYSTEM_TYPE_CONSOLE)
48			seq_printf(m, ":vtmaster");
49		break;
50	case TTY_DRIVER_TYPE_CONSOLE:
51		seq_printf(m, "console");
52		break;
53	case TTY_DRIVER_TYPE_SERIAL:
54		seq_printf(m, "serial");
55		break;
56	case TTY_DRIVER_TYPE_PTY:
57		if (p->subtype == PTY_TYPE_MASTER)
58			seq_printf(m, "pty:master");
59		else if (p->subtype == PTY_TYPE_SLAVE)
60			seq_printf(m, "pty:slave");
61		else
62			seq_printf(m, "pty");
63		break;
64	default:
65		seq_printf(m, "type:%d.%d", p->type, p->subtype);
66	}
67	seq_putc(m, '\n');
68}
69
70static int show_tty_driver(struct seq_file *m, void *v)
71{
72	struct tty_driver *p = v;
73	dev_t from = MKDEV(p->major, p->minor_start);
74	dev_t to = from + p->num;
75
76	if (&p->tty_drivers == tty_drivers.next) {
77		/* pseudo-drivers first */
78		seq_printf(m, "%-20s /dev/%-8s ", "/dev/tty", "tty");
79		seq_printf(m, "%3d %7d ", TTYAUX_MAJOR, 0);
80		seq_printf(m, "system:/dev/tty\n");
81		seq_printf(m, "%-20s /dev/%-8s ", "/dev/console", "console");
82		seq_printf(m, "%3d %7d ", TTYAUX_MAJOR, 1);
83		seq_printf(m, "system:console\n");
84#ifdef CONFIG_UNIX98_PTYS
85		seq_printf(m, "%-20s /dev/%-8s ", "/dev/ptmx", "ptmx");
86		seq_printf(m, "%3d %7d ", TTYAUX_MAJOR, 2);
87		seq_printf(m, "system\n");
88#endif
89#ifdef CONFIG_VT
90		seq_printf(m, "%-20s /dev/%-8s ", "/dev/vc/0", "vc/0");
91		seq_printf(m, "%3d %7d ", TTY_MAJOR, 0);
92		seq_printf(m, "system:vtmaster\n");
93#endif
94	}
95
96	while (MAJOR(from) < MAJOR(to)) {
97		dev_t next = MKDEV(MAJOR(from)+1, 0);
98		show_tty_range(m, p, from, next - from);
99		from = next;
100	}
101	if (from != to)
102		show_tty_range(m, p, from, to - from);
103	return 0;
104}
105
106/* iterator */
107static void *t_start(struct seq_file *m, loff_t *pos)
108{
109	struct list_head *p;
110	loff_t l = *pos;
111
112	mutex_lock(&tty_mutex);
113	list_for_each(p, &tty_drivers)
114		if (!l--)
115			return list_entry(p, struct tty_driver, tty_drivers);
116	return NULL;
117}
118
119static void *t_next(struct seq_file *m, void *v, loff_t *pos)
120{
121	struct list_head *p = ((struct tty_driver *)v)->tty_drivers.next;
122	(*pos)++;
123	return p==&tty_drivers ? NULL :
124			list_entry(p, struct tty_driver, tty_drivers);
125}
126
127static void t_stop(struct seq_file *m, void *v)
128{
129	mutex_unlock(&tty_mutex);
130}
131
132static struct seq_operations tty_drivers_op = {
133	.start	= t_start,
134	.next	= t_next,
135	.stop	= t_stop,
136	.show	= show_tty_driver
137};
138
139static int tty_drivers_open(struct inode *inode, struct file *file)
140{
141	return seq_open(file, &tty_drivers_op);
142}
143
144static const struct file_operations proc_tty_drivers_operations = {
145	.open		= tty_drivers_open,
146	.read		= seq_read,
147	.llseek		= seq_lseek,
148	.release	= seq_release,
149};
150
151/*
152 * This is the handler for /proc/tty/ldiscs
153 */
154static int tty_ldiscs_read_proc(char *page, char **start, off_t off,
155				int count, int *eof, void *data)
156{
157	int	i;
158	int	len = 0;
159	off_t	begin = 0;
160	struct tty_ldisc *ld;
161
162	for (i=0; i < NR_LDISCS; i++) {
163		ld = tty_ldisc_get(i);
164		if (ld == NULL)
165			continue;
166		len += sprintf(page+len, "%-10s %2d\n",
167			       ld->name ? ld->name : "???", i);
168		tty_ldisc_put(i);
169		if (len+begin > off+count)
170			break;
171		if (len+begin < off) {
172			begin += len;
173			len = 0;
174		}
175	}
176	if (i >= NR_LDISCS)
177		*eof = 1;
178	if (off >= len+begin)
179		return 0;
180	*start = page + (off-begin);
181	return ((count < begin+len-off) ? count : begin+len-off);
182}
183
184/*
185 * This function is called by tty_register_driver() to handle
186 * registering the driver's /proc handler into /proc/tty/driver/<foo>
187 */
188void proc_tty_register_driver(struct tty_driver *driver)
189{
190	struct proc_dir_entry *ent;
191
192	if ((!driver->read_proc && !driver->write_proc) ||
193	    !driver->driver_name ||
194	    driver->proc_entry)
195		return;
196
197	ent = create_proc_entry(driver->driver_name, 0, proc_tty_driver);
198	if (!ent)
199		return;
200	ent->read_proc = driver->read_proc;
201	ent->write_proc = driver->write_proc;
202	ent->owner = driver->owner;
203	ent->data = driver;
204
205	driver->proc_entry = ent;
206}
207
208/*
209 * This function is called by tty_unregister_driver()
210 */
211void proc_tty_unregister_driver(struct tty_driver *driver)
212{
213	struct proc_dir_entry *ent;
214
215	ent = driver->proc_entry;
216	if (!ent)
217		return;
218
219	remove_proc_entry(driver->driver_name, proc_tty_driver);
220
221	driver->proc_entry = NULL;
222}
223
224/*
225 * Called by proc_root_init() to initialize the /proc/tty subtree
226 */
227void __init proc_tty_init(void)
228{
229	struct proc_dir_entry *entry;
230	if (!proc_mkdir("tty", NULL))
231		return;
232	proc_tty_ldisc = proc_mkdir("tty/ldisc", NULL);
233	/*
234	 * /proc/tty/driver/serial reveals the exact character counts for
235	 * serial links which is just too easy to abuse for inferring
236	 * password lengths and inter-keystroke timings during password
237	 * entry.
238	 */
239	proc_tty_driver = proc_mkdir_mode("tty/driver", S_IRUSR | S_IXUSR, NULL);
240
241	create_proc_read_entry("tty/ldiscs", 0, NULL, tty_ldiscs_read_proc, NULL);
242	entry = create_proc_entry("tty/drivers", 0, NULL);
243	if (entry)
244		entry->proc_fops = &proc_tty_drivers_operations;
245}
246