[6/6] tty/sysrq: Add configurable handler to execute a compound action
diff mbox series

Message ID 20200511135918.8203-7-andrzej.p@collabora.com
State New
Headers show
Series
  • Magic SysRq extensions
Related show

Commit Message

Andrzej Pietrasiewicz May 11, 2020, 1:59 p.m. UTC
Some userland might want to execute e.g. 'w' (show blocked tasks), followed
by 's' (sync), followed by 1000 ms delay and then followed by 'c' (crash)
upon a single magic SysRq. Or one might want to execute the famous "Raising
Elephants Is So Utterly Boring" action. This patch adds a configurable
handler, triggered with 'C', for this exact purpose. The user specifies the
composition of the compound action using syntax similar to getopt, where
each letter corresponds to an individual action and a colon followed by a
number corresponds to a delay of that many milliseconds, e.g.:

ws:1000c

or

r:100eis:1000ub

Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@collabora.com>
---
 drivers/tty/sysrq.c | 73 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 72 insertions(+), 1 deletion(-)

Comments

Greg Kroah-Hartman May 11, 2020, 4:21 p.m. UTC | #1
On Mon, May 11, 2020 at 03:59:18PM +0200, Andrzej Pietrasiewicz wrote:
> Some userland might want to execute e.g. 'w' (show blocked tasks), followed
> by 's' (sync), followed by 1000 ms delay and then followed by 'c' (crash)
> upon a single magic SysRq. Or one might want to execute the famous "Raising
> Elephants Is So Utterly Boring" action. This patch adds a configurable
> handler, triggered with 'C', for this exact purpose. The user specifies the
> composition of the compound action using syntax similar to getopt, where
> each letter corresponds to an individual action and a colon followed by a
> number corresponds to a delay of that many milliseconds, e.g.:
> 
> ws:1000c
> 
> or
> 
> r:100eis:1000ub

Cute, but why?  Who needs/wants this type of thing?

And again, no documentation :(

greg k-h
Dmitry Torokhov May 11, 2020, 6:29 p.m. UTC | #2
On Mon, May 11, 2020 at 06:21:13PM +0200, Greg Kroah-Hartman wrote:
> On Mon, May 11, 2020 at 03:59:18PM +0200, Andrzej Pietrasiewicz wrote:
> > Some userland might want to execute e.g. 'w' (show blocked tasks), followed
> > by 's' (sync), followed by 1000 ms delay and then followed by 'c' (crash)
> > upon a single magic SysRq. Or one might want to execute the famous "Raising
> > Elephants Is So Utterly Boring" action. This patch adds a configurable
> > handler, triggered with 'C', for this exact purpose. The user specifies the
> > composition of the compound action using syntax similar to getopt, where
> > each letter corresponds to an individual action and a colon followed by a
> > number corresponds to a delay of that many milliseconds, e.g.:
> > 
> > ws:1000c
> > 
> > or
> > 
> > r:100eis:1000ub
> 
> Cute, but why?  Who needs/wants this type of thing?

On Chrome OS the first time user presses SysRq-X it will try to kill
chrome (and that will cause crash to get uploaded if user consented).
The 2nd time within 5 seconds the same combo is pressed, it will dump
blocked tasks in syslog and try to sync and then panic. On panic the
device will reboot, logs will be scraped from pstore, and uploaded for
analysis.

Thanks.
Andrzej Pietrasiewicz May 12, 2020, 9:15 a.m. UTC | #3
Hi,

W dniu 11.05.2020 o 20:29, Dmitry Torokhov pisze:
> On Mon, May 11, 2020 at 06:21:13PM +0200, Greg Kroah-Hartman wrote:
>> On Mon, May 11, 2020 at 03:59:18PM +0200, Andrzej Pietrasiewicz wrote:
>>> Some userland might want to execute e.g. 'w' (show blocked tasks), followed
>>> by 's' (sync), followed by 1000 ms delay and then followed by 'c' (crash)
>>> upon a single magic SysRq. Or one might want to execute the famous "Raising
>>> Elephants Is So Utterly Boring" action. This patch adds a configurable
>>> handler, triggered with 'C', for this exact purpose. The user specifies the
>>> composition of the compound action using syntax similar to getopt, where
>>> each letter corresponds to an individual action and a colon followed by a
>>> number corresponds to a delay of that many milliseconds, e.g.:
>>>
>>> ws:1000c
>>>
>>> or
>>>
>>> r:100eis:1000ub
>>
>> Cute, but why?  Who needs/wants this type of thing?

Surely things that can be done in userspace should be done there.
So we would envision an input daemon which reacts to a predefined
combination of keys. That said, it is not unimaginable to think of
userspace being dead enough (e.g. due to memory pressure) to be unable
to complete such a compound action. In other words userspace not being
able to do it is a good reason for putting the code in the kernel.

Dmitry has given a use case where such a compound action is needed.

Andrzej

> 
> On Chrome OS the first time user presses SysRq-X it will try to kill
> chrome (and that will cause crash to get uploaded if user consented).
> The 2nd time within 5 seconds the same combo is pressed, it will dump
> blocked tasks in syslog and try to sync and then panic. On panic the
> device will reboot, logs will be scraped from pstore, and uploaded for
> analysis.
> 
> Thanks.
>

Patch
diff mbox series

diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index a6e91e4ae304..bde8de2d5b17 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -19,6 +19,7 @@ 
 #include <linux/sched/rt.h>
 #include <linux/sched/debug.h>
 #include <linux/sched/task.h>
+#include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/mm.h>
 #include <linux/fs.h>
@@ -446,6 +447,15 @@  static struct sysrq_key_op sysrq_signal_configured_op = {
 	.enable_mask	= SYSRQ_ENABLE_SIGNAL,
 };
 
+static void sysrq_action_compound(int key);
+
+static struct sysrq_key_op sysrq_action_compound_op = {
+	.handler	= sysrq_action_compound,
+	.help_msg	= "execute-compound-action(C)",
+	.action_msg	= "Execute compound action",
+	.enable_mask	= SYSRQ_ENABLE_SIGNAL,
+};
+
 /* Key Operations table and lock */
 static DEFINE_SPINLOCK(sysrq_key_table_lock);
 
@@ -508,7 +518,7 @@  static struct sysrq_key_op *sysrq_key_table[62] = {
 	&sysrq_ftrace_dump_op,		/* z */
 	NULL,				/* A */
 	NULL,				/* B */
-	NULL,				/* C */
+	&sysrq_action_compound_op,	/* C */
 	NULL,				/* D */
 	NULL,				/* E */
 	NULL,				/* F */
@@ -646,6 +656,7 @@  static char *sysrq_signalled;
 static char *sysrq_signalled_parent;
 static char *sysrq_signal;
 static int sysrq_signal_code;
+static char *sysrq_compound_action;
 
 /* Simple translation table for the SysRq keys */
 static const unsigned char sysrq_xlate[KEY_CNT] =
@@ -864,6 +875,61 @@  static void sysrq_signal_configured(int key)
 	read_unlock(&tasklist_lock);
 }
 
+#define SYSRQ_COMPOUND_ACTION_VALIDATE	0
+#define SYSRQ_COMPOUND_ACTION_RUN	1
+
+static int sysrq_process_compound_action(int pass)
+{
+	const char *action = sysrq_compound_action;
+	struct sysrq_key_op *op_p;
+	int ret, delay;
+
+	while (*action) {
+		op_p = __sysrq_get_key_op(*action);
+		if (!op_p)
+			return -EINVAL;
+
+		/* Don't allow calling ourselves recursively */
+		if (op_p == &sysrq_action_compound_op)
+			return -EINVAL;
+
+		if (pass == SYSRQ_COMPOUND_ACTION_RUN)
+			__handle_sysrq(*action, false);
+
+		if (*++action == ':') {
+			ret = sscanf(action++, ":%d", &delay);
+			if (ret < 1) /* we want at least ":[0-9]" => 1 item */
+				return -EINVAL;
+
+			while (*action >= '0' && *action <= '9')
+				++action;
+			if (pass == SYSRQ_COMPOUND_ACTION_RUN)
+				mdelay(delay);
+		}
+	}
+
+	return 0;
+}
+
+static void sysrq_action_compound(int key)
+{
+	if (!sysrq_compound_action) {
+		pr_err("Unconfigured compound action for %s",
+		       sysrq_action_compound_op.help_msg);
+
+		return;
+	}
+
+	if (sysrq_process_compound_action(SYSRQ_COMPOUND_ACTION_VALIDATE)) {
+		pr_err("Incorrect compound action %s for %s",
+		       sysrq_compound_action,
+		       sysrq_action_compound_op.help_msg);
+
+		return;
+	}
+
+	sysrq_process_compound_action(SYSRQ_COMPOUND_ACTION_RUN);
+}
 
 static void sysrq_reinject_alt_sysrq(struct work_struct *work)
 {
@@ -1165,12 +1231,17 @@  module_param(sysrq_signalled, charp, 0644);
 module_param(sysrq_signalled_parent, charp, 0644);
 module_param(sysrq_signal, charp, 0644);
 
+module_param(sysrq_compound_action, charp, 0644);
 #else
 
 static void sysrq_signal_configured(int key)
 {
 }
 
+static void sysrq_action_compound(int key)
+{
+}
+
 static inline void sysrq_register_handler(void)
 {
 }