From: Samo Pogacnik on
Hi,

This is another version of former detour TTY driver, trying to follow
Alan's suggestions:
- renamed from detour to ttyprintk
- removed possibility for initial internal redirection of console to
this tty
- providing hopefully more proper open/close tty operations
- fixed initial termios settings
- added primitive write_room operation
- added ratelimting support (recursion check) and an ioctl trap to catch
ratelimiting being activated. i'd appreciate a suggestion on this ioctl
command naming/value and on location of its definition?
- additional error checking

I also have a question about the tioccons() function from the tty_io.c.
Should this function also check, if the redirection tty has been opened
for writing, since there are going to be implicit writes to this tty for
each console message? This would prevent users without write permissions
to successfully perform TIOCCONS ioctl. This would also prevent writing
capable users to successfully perform TIOCCONS with read-only opened
tty, but failing to write console messages to the redirection tty
afterwards. This seems to happen because the mode of opened tty at the
time of issuing TIOCCONS ioctl is remembered.

Alan, could you please give the patch bellow another look?

thanks, Samo

---
Signed-off-by: Samo Pogacnik <samo_pogacnik(a)t-2.net>
diff --git a_linux/Documentation/devices.txt b_linux/Documentation/devices.txt
index 53d64d3..71aef33 100644
--- a_linux/Documentation/devices.txt
+++ b_linux/Documentation/devices.txt
@@ -239,6 +239,7 @@ Your cooperation is appreciated.
0 = /dev/tty Current TTY device
1 = /dev/console System console
2 = /dev/ptmx PTY master multiplex
+ 3 = /dev/ttyprintk User messages via printk TTY device
64 = /dev/cua0 Callout device for ttyS0
...
255 = /dev/cua191 Callout device for ttyS191
diff --git a_linux/drivers/char/Kconfig b_linux/drivers/char/Kconfig
index 3141dd3..5c38a06 100644
--- a_linux/drivers/char/Kconfig
+++ b_linux/drivers/char/Kconfig
@@ -485,6 +485,20 @@ config LEGACY_PTY_COUNT
When not in use, each legacy PTY occupies 12 bytes on 32-bit
architectures and 24 bytes on 64-bit architectures.

+config TTY_PRINTK
+ bool "TTY driver to output user messages via printk"
+ default n
+ ---help---
+ If you say Y here, the support for writing user messages (i.e.
+ console messages) via printk is available.
+
+ The feature is useful to inline user messages with kernel
+ messages.
+ In order to use this feature, you should output user messages
+ to /dev/ttyprintk or redirect console to this TTY.
+
+ If unsure, say N.
+
config BRIQ_PANEL
tristate 'Total Impact briQ front panel driver'
depends on PPC_CHRP
diff --git a_linux/drivers/char/Makefile b_linux/drivers/char/Makefile
index f957edf..ed60f45 100644
--- a_linux/drivers/char/Makefile
+++ b_linux/drivers/char/Makefile
@@ -11,6 +11,7 @@ obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o tty_buffer.o t

obj-$(CONFIG_LEGACY_PTYS) += pty.o
obj-$(CONFIG_UNIX98_PTYS) += pty.o
+obj-$(CONFIG_TTY_PRINTK) += ttyprintk.o
obj-y += misc.o
obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o selection.o keyboard.o
obj-$(CONFIG_BFIN_JTAG_COMM) += bfin_jtag_comm.o
diff --git a_linux/drivers/char/ttyprintk.c b_linux/drivers/char/ttyprintk.c
new file mode 100644
index 0000000..5e569e4
--- /dev/null
+++ b_linux/drivers/char/ttyprintk.c
@@ -0,0 +1,305 @@
+/*
+ * linux/drivers/char/ttyprintk.c
+ *
+ * Copyright (C) 2010 Samo Pogacnik
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the smems of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ */
+
+/*
+ * This pseudo device allows user to make printk messages. It is possible
+ * to store "console" messages inline with kernel messages for better analyses
+ * of the boot process, for example.
+ */
+
+#include <linux/sched.h>
+#include <linux/ratelimit.h>
+#include <linux/device.h>
+#include <linux/serial.h>
+#include <linux/tty.h>
+
+/*
+ * Ratelimiting support to handle to much output to this device,
+ * because of explicit writes or because of unintentional recursive
+ * setup (caught printks again sent to this device).
+ */
+static struct ratelimit_state ttyprintk_rs = {
+ .interval = DEFAULT_RATELIMIT_INTERVAL,
+ .burst = DEFAULT_RATELIMIT_BURST,
+};
+
+/*
+ * Ratelimiting action and notification support
+ */
+static DECLARE_WAIT_QUEUE_HEAD(ttyprintk_ratelimit_wq);
+static int ttyprintk_ratelimit_event;
+
+#define ttyprintk_ratelimited(fmt, ...) \
+{ \
+ if (__ratelimit(&ttyprintk_rs)) { \
+ ttyprintk_ratelimit_event = 0; \
+ printk(KERN_INFO fmt, ##__VA_ARGS__); \
+ } else { \
+ ttyprintk_ratelimit_event = 1; \
+ wake_up_all(&ttyprintk_ratelimit_wq); \
+ } \
+}
+
+/*
+ * Our simple preformatting supports transparent output of (time-stamped)
+ * printk messages (also suitable for logging service):
+ * - any cr is replaced by nl
+ * - adds a ttyprintk source tag in front of each line
+ * - too long message is fragmeted, with '\'nl between fragments
+ */
+static const char *tpk_tag = "[U] "; /* U for User */
+#define TTY_PRINTK_STR_SIZE 508
+
+static int tpk_printk(const unsigned char *buf, int count)
+{
+ static char tmp[TTY_PRINTK_STR_SIZE + 4];
+ static int curr;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ tmp[curr] = buf[i];
+ if (curr < TTY_PRINTK_STR_SIZE) {
+ switch (buf[i]) {
+ case '\r':
+ /* replace cr with nl */
+ tmp[curr + 0] = '\n';
+ tmp[curr + 1] = '\0';
+ ttyprintk_ratelimited("%s%s", tpk_tag, tmp);
+ curr = 0;
+ if (buf[i + 1] == '\n')
+ i++;
+ break;
+ case '\n':
+ tmp[curr + 1] = '\0';
+ ttyprintk_ratelimited("%s%s", tpk_tag, tmp);
+ curr = 0;
+ break;
+ default:
+ curr++;
+ }
+ } else {
+ /* end of tmp buffer reached: cut the message in two */
+ tmp[curr + 1] = '\\';
+ tmp[curr + 2] = '\n';
+ tmp[curr + 3] = '\0';
+ ttyprintk_ratelimited("%s%s", tpk_tag, tmp);
+ curr = 0;
+ }
+ }
+ if (curr > 0) {
+ /* non nl or cr terminated message - add nl */
+ tmp[curr + 0] = '\n';
+ tmp[curr + 1] = '\0';
+ ttyprintk_ratelimited("%s%s", tpk_tag, tmp);
+ curr = 0;
+ }
+
+ return count;
+}
+
+struct ttyprintk_port {
+ struct tty_port port;
+ struct mutex port_write_mutex;
+ spinlock_t lock;
+};
+
+static struct ttyprintk_port tpk_port;
+
+/*
+ * TTY operations open function.
+ */
+static int tpk_open(struct tty_struct *tty, struct file *filp)
+{
+ struct ttyprintk_port *port = &tpk_port;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ port->port.count++;
+ tty->driver_data = port;
+ port->port.tty = tty;
+ clear_bit(TTY_IO_ERROR, &port->port.tty->flags);
+ mutex_init(&port->port_write_mutex);
+ port->port.flags |= ASYNC_INITIALIZED;
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ return 0;
+}
+
+/*
+ * TTY operations close function.
+ */
+static void tpk_close(struct tty_struct *tty, struct file *filp)
+{
+ struct ttyprintk_port *port = tty->driver_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ if (tty_hung_up_p(filp)) {
+ spin_unlock_irqrestore(&port->lock, flags);
+ return;
+ }
+
+ if (tty->count == 1 && port->port.count != 1) {
+ printk(KERN_ERR "tpk_close: bad port count;"
+ " tty->count is 1, port count is %d\n",
+ port->port.count);
+ port->port.count = 1;
+ }
+
+ if (port->port.count > 1) {
+ port->port.count--;
+
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ return;
+ }
+ port->port.flags |= ASYNC_CLOSING;
+ /*
+ * We have no HW to wait for its buffer to clear; and we notify
+ * the line discipline to only process XON/XOFF characters.
+ */
+ tty->closing = 1;
+ spin_unlock_irqrestore(&port->lock, flags);
+ /*
+ * We have no HW to handle to stop accepting input.
+ */
+ if (--port->port.count < 0) {
+ printk(KERN_ERR
+ "tpk_close: bad port usage count for ttyprintk: %d\n",
+ port->port.count);
+ port->port.count = 0;
+ }
+
+ tty_ldisc_flush(tty);
+ spin_lock_irqsave(&port->lock, flags);
+ tty->closing = 0;
+ port->port.tty = NULL;
+ spin_unlock_irqrestore(&port->lock, flags);
+ port->port.flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING);
+}
+
+/*
+ * TTY operations write function.
+ */
+int tpk_write(struct tty_struct *tty,
+ const unsigned char *buf, int count)
+{
+ struct ttyprintk_port *port;
+ int ret;
+
+ port = tty->driver_data;
+
+ if (!port)
+ return 0;
+
+ if (!(port->port.flags & ASYNC_INITIALIZED))
+ return 0;
+
+ /* exclusive use of tpk_printk within this tty */
+ mutex_lock(&port->port_write_mutex);
+ ret = tpk_printk(buf, count);
+ mutex_unlock(&port->port_write_mutex);
+
+ return ret;
+}
+
+/*
+ * TTY operations write_room function.
+ */
+int tpk_write_room(struct tty_struct *tty)
+{
+ return TTY_PRINTK_STR_SIZE;
+}
+
+/*
+ * TTY operations ioctl function.
+ */
+#define TPKRLEV (('e'<<8) | 0) /* Wait for ttyprintk ratelimiting event*/
+static int tpk_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct ttyprintk_port *port;
+
+ port = tty->driver_data;
+
+ if (!port)
+ return -EINVAL;
+
+ switch (cmd) {
+ case TPKRLEV:
+ wait_event_interruptible(ttyprintk_ratelimit_wq,
+ (ttyprintk_ratelimit_event != 0));
+ break;
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static const struct tty_operations ttyprintk_ops = {
+ .open = tpk_open,
+ .close = tpk_close,
+ .write = tpk_write,
+ .write_room = tpk_write_room,
+ .ioctl = tpk_ioctl,
+};
+
+static struct tty_driver *ttyprintk_driver;
+
+static int __init ttyprintk_init(void)
+{
+ int ret = -ENOMEM;
+ void *rp;
+
+ ttyprintk_driver = alloc_tty_driver(1);
+ if (!ttyprintk_driver)
+ return ret;
+
+ ttyprintk_driver->owner = THIS_MODULE;
+ ttyprintk_driver->driver_name = "ttyprintk";
+ ttyprintk_driver->name = "ttyprintk";
+ ttyprintk_driver->major = TTYAUX_MAJOR;
+ ttyprintk_driver->minor_start = 3;
+ ttyprintk_driver->num = 1;
+ ttyprintk_driver->type = TTY_DRIVER_TYPE_CONSOLE;
+ ttyprintk_driver->init_termios = tty_std_termios;
+ ttyprintk_driver->init_termios.c_oflag = OPOST | OCRNL | ONOCR | ONLRET;
+ ttyprintk_driver->flags = TTY_DRIVER_RESET_TERMIOS |
+ TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+ tty_set_operations(ttyprintk_driver, &ttyprintk_ops);
+
+ ret = tty_register_driver(ttyprintk_driver);
+ if (ret < 0) {
+ printk(KERN_ERR "Couldn't register ttyprintk driver\n");
+ goto error;
+ }
+
+ /* create our unnumbered device */
+ rp = device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 3), NULL,
+ ttyprintk_driver->name);
+ if (IS_ERR(rp)) {
+ printk(KERN_ERR "Couldn't create ttyprintk device\n");
+ ret = PTR_ERR(rp);
+ goto error;
+ }
+
+ memset(&tpk_port, 0, sizeof(tpk_port));
+ tty_port_init(&tpk_port.port);
+ spin_lock_init(&tpk_port.lock);
+
+ return 0;
+
+error:
+ put_tty_driver(ttyprintk_driver);
+ ttyprintk_driver = NULL;
+ return ret;
+}
+module_init(ttyprintk_init);



--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Alan Cox on

> +/*
> + * TTY operations open function.
> + */
> +static int tpk_open(struct tty_struct *tty, struct file *filp)
> +{
> + struct ttyprintk_port *port = &tpk_port;

You can replace the rest with

return tty_port_open(port, tty, filp);
}

> + * TTY operations close function.
> + */
> +static void tpk_close(struct tty_struct *tty, struct file *filp)
> +{
> + struct ttyprintk_port *port = tty->driver_data;

and
tty_port_close(port, tty, filp);

> +}

which saves a lot of work

> +
> +/*
> + * TTY operations write function.
> + */
> +int tpk_write(struct tty_struct *tty,
> + const unsigned char *buf, int count)
> +{
> + struct ttyprintk_port *port;
> + int ret;
> +
> + port = tty->driver_data;
> +
> + if (!port)
> + return 0;
> +
> + if (!(port->port.flags & ASYNC_INITIALIZED))
> + return 0;

These two can't happen if you use tty_port_* so it is better to blow up.
If you think you may be seeing it occur then use WARN_ON() or similar

> +
> + /* exclusive use of tpk_printk within this tty */
> + mutex_lock(&port->port_write_mutex);
> + ret = tpk_printk(buf, count);
> + mutex_unlock(&port->port_write_mutex);

And this is serialized by the caller (not that having your own lock is
any harm)


> +#define TPKRLEV (('e'<<8) | 0) /* Wait for ttyprintk ratelimiting event*/
> +static int tpk_ioctl(struct tty_struct *tty, struct file *file,
> + unsigned int cmd, unsigned long arg)
> +{
> + struct ttyprintk_port *port;
> +
> + port = tty->driver_data;
> +
> + if (!port)
> + return -EINVAL;
> +
> + switch (cmd) {
> + case TPKRLEV:
> + wait_event_interruptible(ttyprintk_ratelimit_wq,
> + (ttyprintk_ratelimit_event != 0));

Ok that wasn't quite what I had in mind.

What I was thinking was needed was this

/* Stop TIOCCONS */
case TIOCCONS:
return -EOPNOTSUPP;

only it won't work that way. I'll sort that out in tty_io.c once the
driver is happy. That way anything trying to mis-redirect the console
will get stopped early which is probably more reliable than a ratelimit ?


> + memset(&tpk_port, 0, sizeof(tpk_port));

It's static so that isn't needed

> + tty_port_init(&tpk_port.port);
> + spin_lock_init(&tpk_port.lock);

The one other bit you will need to use the helpers is

struct tty_port_operations null_ops = { };

tpk_port.port->ops = &null_ops;


Alan
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Samo Pogacnik on

> > +#define TPKRLEV (('e'<<8) | 0) /* Wait for ttyprintk ratelimiting event*/
> > +static int tpk_ioctl(struct tty_struct *tty, struct file *file,
> > + unsigned int cmd, unsigned long arg)
> > +{
> > + struct ttyprintk_port *port;
> > +
> > + port = tty->driver_data;
> > +
> > + if (!port)
> > + return -EINVAL;
> > +
> > + switch (cmd) {
> > + case TPKRLEV:
> > + wait_event_interruptible(ttyprintk_ratelimit_wq,
> > + (ttyprintk_ratelimit_event != 0));
>
> Ok that wasn't quite what I had in mind.
>
> What I was thinking was needed was this
>
> /* Stop TIOCCONS */
> case TIOCCONS:
> return -EOPNOTSUPP;
>
> only it won't work that way. I'll sort that out in tty_io.c once the
> driver is happy. That way anything trying to mis-redirect the console
> will get stopped early which is probably more reliable than a ratelimit ?
>
I'm thinking to leave the ratelimit support in for the time being. I had
in mind cases, when someone does
"cat /proc/kmsg > dev/ttyprintk" or
suppose the console is redirected to ttyprintk (which i would like to be
able to do from user program) and then someone does:
"cat /proc/kmsg > /dev/console"... or
if console is redirected after this command ?

Were you thinking of some other mis-redirection case?

Samo

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Samo Pogacnik on

> > +/*
> > + * TTY operations open function.
> > + */
> > +static int tpk_open(struct tty_struct *tty, struct file *filp)
> > +{
> > + struct ttyprintk_port *port = &tpk_port;
>
> You can replace the rest with
>
> return tty_port_open(port, tty, filp);
> }
>
fixed

> > + * TTY operations close function.
> > + */
> > +static void tpk_close(struct tty_struct *tty, struct file *filp)
> > +{
> > + struct ttyprintk_port *port = tty->driver_data;
>
> and
> tty_port_close(port, tty, filp);
>
> > +}
>
> which saves a lot of work
>
yeah right, done

> > +
> > +/*
> > + * TTY operations write function.
> > + */
> > +int tpk_write(struct tty_struct *tty,
> > + const unsigned char *buf, int count)
> > +{
> > + struct ttyprintk_port *port;
> > + int ret;
> > +
> > + port = tty->driver_data;
> > +
> > + if (!port)
> > + return 0;
> > +
> > + if (!(port->port.flags & ASYNC_INITIALIZED))
> > + return 0;
>
> These two can't happen if you use tty_port_* so it is better to blow up.
> If you think you may be seeing it occur then use WARN_ON() or similar
>
the two checks were removed

> > +
> > + /* exclusive use of tpk_printk within this tty */
> > + mutex_lock(&port->port_write_mutex);
> > + ret = tpk_printk(buf, count);
> > + mutex_unlock(&port->port_write_mutex);
>
> And this is serialized by the caller (not that having your own lock is
> any harm)
>
own lock peserved just in case

>
> > +#define TPKRLEV (('e'<<8) | 0) /* Wait for ttyprintk ratelimiting event*/
> > +static int tpk_ioctl(struct tty_struct *tty, struct file *file,
> > + unsigned int cmd, unsigned long arg)
> > +{
> > + struct ttyprintk_port *port;
> > +
> > + port = tty->driver_data;
> > +
> > + if (!port)
> > + return -EINVAL;
> > +
> > + switch (cmd) {
> > + case TPKRLEV:
> > + wait_event_interruptible(ttyprintk_ratelimit_wq,
> > + (ttyprintk_ratelimit_event != 0));
>
> Ok that wasn't quite what I had in mind.
ratelimiting remained present

>
> What I was thinking was needed was this
>
> /* Stop TIOCCONS */
> case TIOCCONS:
> return -EOPNOTSUPP;
>
added

> only it won't work that way. I'll sort that out in tty_io.c once the
> driver is happy. That way anything trying to mis-redirect the console
> will get stopped early which is probably more reliable than a ratelimit ?
>
> > + memset(&tpk_port, 0, sizeof(tpk_port));
>
memset removed

> It's static so that isn't needed
>
> > + tty_port_init(&tpk_port.port);
> > + spin_lock_init(&tpk_port.lock);
>
> The one other bit you will need to use the helpers is
>
> struct tty_port_operations null_ops = { };
>
> tpk_port.port->ops = &null_ops;
>
> Alan
Also added, with many thanks,

Samo
----
Signed-off-by: Samo Pogacnik <samo_pogacnik(a)t-2.net>
diff --git a_linux/Documentation/devices.txt b_linux/Documentation/devices.txt
index 53d64d3..71aef33 100644
--- a_linux/Documentation/devices.txt
+++ b_linux/Documentation/devices.txt
@@ -239,6 +239,7 @@ Your cooperation is appreciated.
0 = /dev/tty Current TTY device
1 = /dev/console System console
2 = /dev/ptmx PTY master multiplex
+ 3 = /dev/ttyprintk User messages via printk TTY device
64 = /dev/cua0 Callout device for ttyS0
...
255 = /dev/cua191 Callout device for ttyS191
diff --git a_linux/drivers/char/Kconfig b_linux/drivers/char/Kconfig
index 3141dd3..5c38a06 100644
--- a_linux/drivers/char/Kconfig
+++ b_linux/drivers/char/Kconfig
@@ -485,6 +485,20 @@ config LEGACY_PTY_COUNT
When not in use, each legacy PTY occupies 12 bytes on 32-bit
architectures and 24 bytes on 64-bit architectures.

+config TTY_PRINTK
+ bool "TTY driver to output user messages via printk"
+ default n
+ ---help---
+ If you say Y here, the support for writing user messages (i.e.
+ console messages) via printk is available.
+
+ The feature is useful to inline user messages with kernel
+ messages.
+ In order to use this feature, you should output user messages
+ to /dev/ttyprintk or redirect console to this TTY.
+
+ If unsure, say N.
+
config BRIQ_PANEL
tristate 'Total Impact briQ front panel driver'
depends on PPC_CHRP
diff --git a_linux/drivers/char/Makefile b_linux/drivers/char/Makefile
index f957edf..ed60f45 100644
--- a_linux/drivers/char/Makefile
+++ b_linux/drivers/char/Makefile
@@ -11,6 +11,7 @@ obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o tty_buffer.o t

obj-$(CONFIG_LEGACY_PTYS) += pty.o
obj-$(CONFIG_UNIX98_PTYS) += pty.o
+obj-$(CONFIG_TTY_PRINTK) += ttyprintk.o
obj-y += misc.o
obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o selection.o keyboard.o
obj-$(CONFIG_BFIN_JTAG_COMM) += bfin_jtag_comm.o
diff --git a_linux/drivers/char/ttyprintk.c b_linux/drivers/char/ttyprintk.c
new file mode 100644
index 0000000..333490d
--- /dev/null
+++ b_linux/drivers/char/ttyprintk.c
@@ -0,0 +1,248 @@
+/*
+ * linux/drivers/char/ttyprintk.c
+ *
+ * Copyright (C) 2010 Samo Pogacnik
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the smems of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ */
+
+/*
+ * This pseudo device allows user to make printk messages. It is possible
+ * to store "console" messages inline with kernel messages for better analyses
+ * of the boot process, for example.
+ */
+
+#include <linux/sched.h>
+#include <linux/ratelimit.h>
+#include <linux/device.h>
+#include <linux/serial.h>
+#include <linux/tty.h>
+
+/*
+ * Ratelimiting support to handle to much output to this device,
+ * because of explicit writes or because of unintentional recursive
+ * setup (caught printks again sent to this device).
+ */
+static struct ratelimit_state ttyprintk_rs = {
+ .interval = DEFAULT_RATELIMIT_INTERVAL,
+ .burst = DEFAULT_RATELIMIT_BURST,
+};
+
+/*
+ * Ratelimiting action and notification support
+ */
+static DECLARE_WAIT_QUEUE_HEAD(ttyprintk_ratelimit_wq);
+static int ttyprintk_ratelimit_event;
+
+#define ttyprintk_ratelimited(fmt, ...) \
+{ \
+ if (__ratelimit(&ttyprintk_rs)) { \
+ ttyprintk_ratelimit_event = 0; \
+ printk(KERN_INFO fmt, ##__VA_ARGS__); \
+ } else { \
+ ttyprintk_ratelimit_event = 1; \
+ wake_up_all(&ttyprintk_ratelimit_wq); \
+ } \
+}
+
+/*
+ * Our simple preformatting supports transparent output of (time-stamped)
+ * printk messages (also suitable for logging service):
+ * - any cr is replaced by nl
+ * - adds a ttyprintk source tag in front of each line
+ * - too long message is fragmeted, with '\'nl between fragments
+ */
+static const char *tpk_tag = "[U] "; /* U for User */
+#define TTY_PRINTK_STR_SIZE 508
+
+static int tpk_printk(const unsigned char *buf, int count)
+{
+ static char tmp[TTY_PRINTK_STR_SIZE + 4];
+ static int curr;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ tmp[curr] = buf[i];
+ if (curr < TTY_PRINTK_STR_SIZE) {
+ switch (buf[i]) {
+ case '\r':
+ /* replace cr with nl */
+ tmp[curr + 0] = '\n';
+ tmp[curr + 1] = '\0';
+ ttyprintk_ratelimited("%s%s", tpk_tag, tmp);
+ curr = 0;
+ if (buf[i + 1] == '\n')
+ i++;
+ break;
+ case '\n':
+ tmp[curr + 1] = '\0';
+ ttyprintk_ratelimited("%s%s", tpk_tag, tmp);
+ curr = 0;
+ break;
+ default:
+ curr++;
+ }
+ } else {
+ /* end of tmp buffer reached: cut the message in two */
+ tmp[curr + 1] = '\\';
+ tmp[curr + 2] = '\n';
+ tmp[curr + 3] = '\0';
+ ttyprintk_ratelimited("%s%s", tpk_tag, tmp);
+ curr = 0;
+ }
+ }
+ if (curr > 0) {
+ /* non nl or cr terminated message - add nl */
+ tmp[curr + 0] = '\n';
+ tmp[curr + 1] = '\0';
+ ttyprintk_ratelimited("%s%s", tpk_tag, tmp);
+ curr = 0;
+ }
+
+ return count;
+}
+
+struct ttyprintk_port {
+ struct tty_port port;
+ struct mutex port_write_mutex;
+};
+
+static struct ttyprintk_port tpk_port;
+
+/*
+ * TTY operations open function.
+ */
+static int tpk_open(struct tty_struct *tty, struct file *filp)
+{
+ tty->driver_data = &tpk_port;
+
+ return tty_port_open(&tpk_port.port, tty, filp);
+}
+
+/*
+ * TTY operations close function.
+ */
+static void tpk_close(struct tty_struct *tty, struct file *filp)
+{
+ struct ttyprintk_port *tpkp = tty->driver_data;
+
+ tty_port_close(&tpkp->port, tty, filp);
+}
+
+/*
+ * TTY operations write function.
+ */
+int tpk_write(struct tty_struct *tty,
+ const unsigned char *buf, int count)
+{
+ struct ttyprintk_port *tpkp = tty->driver_data;
+ int ret;
+
+ /* exclusive use of tpk_printk within this tty */
+ mutex_lock(&tpkp->port_write_mutex);
+ ret = tpk_printk(buf, count);
+ mutex_unlock(&tpkp->port_write_mutex);
+
+ return ret;
+}
+
+/*
+ * TTY operations write_room function.
+ */
+int tpk_write_room(struct tty_struct *tty)
+{
+ return TTY_PRINTK_STR_SIZE;
+}
+
+/*
+ * TTY operations ioctl function.
+ */
+#define TPKRLEV (('e'<<8) | 0) /* Wait for ttyprintk ratelimiting event*/
+static int tpk_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct ttyprintk_port *port;
+
+ port = tty->driver_data;
+
+ if (!port)
+ return -EINVAL;
+
+ switch (cmd) {
+ /* Catch ratelimiting activation */
+ case TPKRLEV:
+ wait_event_interruptible(ttyprintk_ratelimit_wq,
+ (ttyprintk_ratelimit_event != 0));
+ break;
+ /* Stop TIOCCONS */
+ case TIOCCONS:
+ return -EOPNOTSUPP;
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static const struct tty_operations ttyprintk_ops = {
+ .open = tpk_open,
+ .close = tpk_close,
+ .write = tpk_write,
+ .write_room = tpk_write_room,
+ .ioctl = tpk_ioctl,
+};
+
+struct tty_port_operations null_ops = { };
+
+static struct tty_driver *ttyprintk_driver;
+
+static int __init ttyprintk_init(void)
+{
+ int ret = -ENOMEM;
+ void *rp;
+
+ ttyprintk_driver = alloc_tty_driver(1);
+ if (!ttyprintk_driver)
+ return ret;
+
+ ttyprintk_driver->owner = THIS_MODULE;
+ ttyprintk_driver->driver_name = "ttyprintk";
+ ttyprintk_driver->name = "ttyprintk";
+ ttyprintk_driver->major = TTYAUX_MAJOR;
+ ttyprintk_driver->minor_start = 3;
+ ttyprintk_driver->num = 1;
+ ttyprintk_driver->type = TTY_DRIVER_TYPE_CONSOLE;
+ ttyprintk_driver->init_termios = tty_std_termios;
+ ttyprintk_driver->init_termios.c_oflag = OPOST | OCRNL | ONOCR | ONLRET;
+ ttyprintk_driver->flags = TTY_DRIVER_RESET_TERMIOS |
+ TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+ tty_set_operations(ttyprintk_driver, &ttyprintk_ops);
+
+ ret = tty_register_driver(ttyprintk_driver);
+ if (ret < 0) {
+ printk(KERN_ERR "Couldn't register ttyprintk driver\n");
+ goto error;
+ }
+
+ /* create our unnumbered device */
+ rp = device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 3), NULL,
+ ttyprintk_driver->name);
+ if (IS_ERR(rp)) {
+ printk(KERN_ERR "Couldn't create ttyprintk device\n");
+ ret = PTR_ERR(rp);
+ goto error;
+ }
+
+ tty_port_init(&tpk_port.port);
+ tpk_port.port.ops = &null_ops;
+ mutex_init(&tpk_port.port_write_mutex);
+
+ return 0;
+
+error:
+ put_tty_driver(ttyprintk_driver);
+ ttyprintk_driver = NULL;
+ return ret;
+}
+module_init(ttyprintk_init);


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Alan Cox on
> I'm thinking to leave the ratelimit support in for the time being. I had
> in mind cases, when someone does
> "cat /proc/kmsg > dev/ttyprintk" or
> suppose the console is redirected to ttyprintk (which i would like to be
> able to do from user program)

Console as in the printk sense would then loop.

If you are going to do the flow control you should probably do something
like


write_room()
{
if (!flow_controlled)
space = 8192;
return space;
}

write()
{
space -= len;
}

then your flow control will behave properly and slow down users rather
than losing data (except stuff sent without blocking)

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/