Unable to fake terminal input with termios.TIOCSTI

前端 未结 2 621
北恋
北恋 2020-12-15 01:47

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 c

相关标签:
2条回答
  • 2020-12-15 02:31

    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
    
    0 讨论(0)
  • 2020-12-15 02:36

    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).

    0 讨论(0)
提交回复
热议问题