summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2026-02-28 09:03:27 -0800
committerPaul Eggert <eggert@cs.ucla.edu>2026-02-28 09:09:22 -0800
commitaed1c8b536534f45aad352ee47ed75a3aa18bc39 (patch)
tree812b1fec754f8874ff32888d3940137b63cc8ed1
parent936472345d6a152ff1f7f863f6d199371f623f28 (diff)
Don’t stuff keyboard input uselessly
Also, document stuffing better. * src/keyboard.c (stuff_buffered_input): Give up on stuffing if it fails. * src/sysdep.c (stuff_char): Return failure indication.
-rw-r--r--admin/notes/multi-tty2
-rw-r--r--doc/lispref/os.texi18
-rw-r--r--src/emacs.c6
-rw-r--r--src/keyboard.c46
-rw-r--r--src/lisp.h2
-rw-r--r--src/sysdep.c17
6 files changed, 42 insertions, 49 deletions
diff --git a/admin/notes/multi-tty b/admin/notes/multi-tty
index fff20a1769b..c4273fd4431 100644
--- a/admin/notes/multi-tty
+++ b/admin/notes/multi-tty
@@ -504,6 +504,8 @@ THINGS TO DO
** flow-ctrl.el must be updated.
** Fix stuff_char for multi-tty. Doesn't seem to be of high priority.
+ For security reasons stuff_char does not work by default on many
+ platforms, which makes such a fix even lower priority.
DIARY OF CHANGES
----------------
diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi
index bb768fe2da8..38d7ad44c4a 100644
--- a/doc/lispref/os.texi
+++ b/doc/lispref/os.texi
@@ -711,12 +711,11 @@ If @var{exit-data} is an integer, that is used as the exit status of
the Emacs process. (This is useful primarily in batch operation; see
@ref{Batch Mode}.)
-If @var{exit-data} is a string, its contents are stuffed into the
-terminal input buffer so that the shell (or whatever program next reads
-input) can read them. This is only possible on some systems. Note that
-recent versions of GNU/Linux disable the system API required for
-stuffing the string into the terminal input, so this might not work on
-your system without special privileges.
+If @var{exit-data} is a string, its contents (followed by a newline) are
+stuffed into the terminal input buffer so that the shell (or whatever
+program next reads input) can read them. However, stuffing is silently
+skipped if Emacs is not running interactively on a terminal, or if
+stuffing requires special privileges or is not supported on this platform.
If @var{exit-data} is neither an integer nor a string, or is omitted,
that means to use the (system-specific) exit status which indicates
@@ -816,6 +815,9 @@ before suspending Emacs, or this function signals an error.
If @var{string} is non-@code{nil}, its characters are sent to Emacs's
superior shell, to be read as terminal input.
+However, this action is skipped if Emacs is not running interactively on
+a terminal, or if stuffing characters into the terminal input requires
+special privileges or is not supported on this platform.
Before suspending, @code{suspend-emacs} runs the normal hook
@code{suspend-hook}. After the user resumes Emacs,
@@ -840,7 +842,7 @@ Here is an example of how you could use these hooks:
@c The sit-for prevents the @code{nil} that suspend-emacs returns
@c hiding the message.
-Here is what you would see upon evaluating @code{(suspend-emacs "pwd")}:
+Here is what you would see upon evaluating @code{(suspend-emacs)}:
@smallexample
@group
@@ -851,8 +853,6 @@ Really suspend? @kbd{y}
@group
---------- Parent Shell ----------
-bash$ pwd
-/home/username
bash$ fg
@end group
diff --git a/src/emacs.c b/src/emacs.c
index 777ade9d825..6929886424f 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -2947,9 +2947,9 @@ sort_args (int argc, char **argv)
DEFUN ("kill-emacs", Fkill_emacs, Skill_emacs, 0, 2, "P",
doc: /* Exit the Emacs job and kill it.
If ARG is an integer, return ARG as the exit program code.
-If ARG is a string, stuff it as keyboard input. (This might
-not work on modern systems due to security considerations, or
-not at all.)
+If ARG is a string, stuff it and then a newline as keyboard input,
+if Emacs is running interactively on a terminal and the platform
+supports and allows stuffing; this may need special privileges.
Any other value of ARG, or ARG omitted, means return an
exit code that indicates successful program termination.
diff --git a/src/keyboard.c b/src/keyboard.c
index 648dd81660a..55fb2401f2b 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -12421,19 +12421,15 @@ DEFUN ("suspend-emacs", Fsuspend_emacs, Ssuspend_emacs, 0, 1, "",
If `cannot-suspend' is non-nil, or if the system doesn't support job
control, run a subshell instead.
-If optional arg STUFFSTRING is non-nil, its characters are stuffed
-to be read as terminal input by Emacs's parent, after suspension.
+If optional arg STUFFSTRING is non-nil, stuff it and then a newline as
+keyboard input, if Emacs is running interactively on a terminal and the
+platform supports and allows stuffing; this may need special privileges.
Before suspending, run the normal hook `suspend-hook'.
After resumption run the normal hook `suspend-resume-hook'.
Some operating systems cannot stop the Emacs process and resume it later.
-On such systems, Emacs starts a subshell instead of suspending.
-
-On some operating systems, stuffing characters into terminal input
-buffer requires special privileges or is not supported at all.
-On such systems, calling this function with non-nil STUFFSTRING might
-either signal an error or silently fail to stuff the characters. */)
+On such systems, Emacs starts a subshell instead of suspending. */)
(Lisp_Object stuffstring)
{
specpdl_ref count = SPECPDL_INDEX ();
@@ -12472,38 +12468,34 @@ either signal an error or silently fail to stuff the characters. */)
return Qnil;
}
-/* If STUFFSTRING is a string, stuff its contents as pending terminal input.
- Then in any case stuff anything Emacs has read ahead and not used. */
+/* If STUFFSTRING is a string, stuff its contents and then a newline as
+ pending terminal input. Then stuff anything Emacs has read ahead and
+ not used. However, do nothing if stuffing does not work. */
void
stuff_buffered_input (Lisp_Object stuffstring)
{
#ifdef SIGTSTP /* stuff_char is defined if SIGTSTP. */
- register unsigned char *p;
+ int bad_stuff = 0;
if (STRINGP (stuffstring))
{
- register ptrdiff_t count;
-
- p = SDATA (stuffstring);
- count = SBYTES (stuffstring);
- while (count-- > 0)
- stuff_char (*p++);
- stuff_char ('\n');
+ char *p = SSDATA (stuffstring);
+ for (ptrdiff_t i = SBYTES (stuffstring); !bad_stuff && 0 < i; i--)
+ bad_stuff = stuff_char (*p++);
+ if (!bad_stuff)
+ bad_stuff = stuff_char ('\n');
}
- /* Anything we have read ahead, put back for the shell to read. */
- /* ?? What should this do when we have multiple keyboards??
- Should we ignore anything that was typed in at the "wrong" kboard?
-
- rms: we should stuff everything back into the kboard
- it came from. */
+ /* Anything we have read ahead, put back for the shell to read.
+ When we have multiple keyboards, we should stuff everything back
+ into the keyboard it came from, but fixing this is low priority as
+ many systems prohibit stuffing for security reasons. */
for (; kbd_fetch_ptr != kbd_store_ptr;
kbd_fetch_ptr = next_kbd_event (kbd_fetch_ptr))
{
-
- if (kbd_fetch_ptr->kind == ASCII_KEYSTROKE_EVENT)
- stuff_char (kbd_fetch_ptr->ie.code);
+ if (kbd_fetch_ptr->kind == ASCII_KEYSTROKE_EVENT && !bad_stuff)
+ bad_stuff = stuff_char (kbd_fetch_ptr->ie.code);
clear_event (&kbd_fetch_ptr->ie);
}
diff --git a/src/lisp.h b/src/lisp.h
index cefca3d6fbb..37c34dfbe84 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -5280,7 +5280,7 @@ maybe_disable_address_randomization (int argc, char **argv)
extern int emacs_exec_file (char const *, char *const *, char *const *);
extern void init_standard_fds (void);
extern char *emacs_get_current_dir_name (void);
-extern void stuff_char (char c);
+extern int stuff_char (char c);
extern void init_foreground_group (void);
extern void sys_subshell (void);
extern void sys_suspend (void);
diff --git a/src/sysdep.c b/src/sysdep.c
index 2f5572009fb..8895655566e 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -390,23 +390,22 @@ discard_tty_input (void)
/* Arrange for character C to be read as the next input from
the terminal.
+ Return 0 on success, -1 otherwise.
XXX What if we have multiple ttys?
*/
-void
+int
stuff_char (char c)
{
- if (! (FRAMEP (selected_frame)
- && FRAME_LIVE_P (XFRAME (selected_frame))
- && FRAME_TERMCAP_P (XFRAME (selected_frame))))
- return;
-
/* Should perhaps error if in batch mode */
#ifdef TIOCSTI
- ioctl (fileno (CURTTY()->input), TIOCSTI, &c);
-#else /* no TIOCSTI */
- error ("Cannot stuff terminal input characters in this version of Unix");
+ if (FRAMEP (selected_frame)
+ && FRAME_LIVE_P (XFRAME (selected_frame))
+ && FRAME_TERMCAP_P (XFRAME (selected_frame)))
+ return ioctl (fileno (CURTTY()->input), TIOCSTI, &c);
#endif /* no TIOCSTI */
+
+ return -1;
}
#endif /* SIGTSTP */