Most of the code samples I've seen are trying to read from stdin without local echo. To do this they modify the "local modes" flag to remove the setting to "Echo input characters". I thought I could just modify the "input modes" flag to TIOCSTI
which is for "Insert the given byte in the input queue.". However, even though I run the script as root, it has no effect. anything I write to the fd seems to go to the terminal output, rather than the terminal input. Basically what I want to do is this exact thing, but in pure python.
"""
termfake.py
Usage: sudo python termfake.py /dev/ttys002
Get the tty device path of a different local termimal by running `tty`
in that terminal.
"""
import sys
import termios
fd = open(sys.argv[1], 'w')
fdno = fd.fileno()
# Returns [iflag, oflag, cflag, lflag, ispeed, ospeed, cc]
tatters = termios.tcgetattr(fdno)
print('original', tatters)
tatters[0] = termios.TIOCSTI
print('TIOCSTI', termios.TIOCSTI)
# Set iflag
termios.tcsetattr(fdno, termios.TCSANOW, tatters)
# Verify setting change
with open('/dev/ttys002', 'w') as fd2:
print('modified', termios.tcgetattr(fd2.fileno()))
fd.write('This is test\n')
fd.close()
TIOCSTI
is an ioctl (documented in tty_ioctl(4)), not a terminal setting, so you can't use tcsetattr()
-- you need to feed each character of the fake input to ioctl()
instead. Never had to do ioctl's from Python before, but the following seems to work for running an ls
in a different terminal (specified as the argument, e.g. /dev/pts/13) that's running Bash:
import fcntl
import sys
import termios
with open(sys.argv[1], 'w') as fd:
for c in "ls\n":
fcntl.ioctl(fd, termios.TIOCSTI, c)
TIOCSTI
requires root privileges (or CAP_SYS_ADMIN
to be more specific, but that's usually the same in practice) by the way -- see capabilities(7).
I took the answer from @Ulfalizer and expanded it a bit to be a complete and usable app.
import sys
import fcntl
import termios
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('tty', type=argparse.FileType('w'),
help='full tty path as given by the tty command')
group = parser.add_mutually_exclusive_group()
group.add_argument('-n', action='store_true',
help='prevent sending a trailing newline character')
group.add_argument('--stdin', action='store_true',
help='read input from stdin')
group = parser.add_argument_group()
group.add_argument('cmd', nargs='?',
help='command to run (required if not using --stdin)')
group.add_argument('args', nargs='*',
help='arguments to command')
args = parser.parse_known_args()
if args.stdin:
data = sys.stdin.read()
else:
data = ' '.join([args.cmd] + args.args)
for c in data:
fcntl.ioctl(args.tty, termios.TIOCSTI, c)
if not args.n and data[-1][-1] != '\n':
fcntl.ioctl(args.tty, termios.TIOCSTI, '\n')
Here is how you use it:
Terminal #1: do...
$ tty > /tmp/t1
Terminal #2: do...
$ sudo python termfake.py $(cat /tmp/t1) date +%s
Terminal #1: observe...
$ tty > /tmp/t1
$ date +%s
1487276400
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With