Utility to Send Commands or Data to Other Terminals (tty/pts)

I have been working on a solution to mirror the PWD of multiple terminals. As part of that I need a way to execute commands on other tty/pts. A simple echo won’t work because echo writes to the output buffer, while I need to push these commands to the input buffer of the tty/pts. I found this forum post after some googling. I adapted the code from the forum post for my purpose.

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <string.h>
#include <unistd.h>

void print_help(char *prog_name) {
        printf("Usage: %s [-n] DEVNAME COMMAND\n", prog_name);
        printf("Usage: '-n' is an optional argument if you want to push a new line at the end of the text\n");
        printf("Usage: Will require 'sudo' to run if the executable is not setuid root\n");
        exit(1);
}

int main (int argc, char *argv[]) {
    char *cmd, *nl = "\n";
    int i, fd;
    int devno, commandno, newline;
    int mem_len;
    devno = 1; commandno = 2; newline = 0;
    if (argc < 3) {
        print_help(argv[0]);
    }
    if (argc > 3 && argv[1][0] == '-' && argv[1][1] == 'n') {
        devno = 2; commandno = 3; newline=1;
    } else if (argc > 3 && argv[1][0] == '-' && argv[1][1] != 'n') {
        printf("Invalid Option\n");
        print_help(argv[0]);
    }
    fd = open(argv[devno],O_RDWR);
    if(fd == -1) {
        perror("open DEVICE");
        exit(1);
    }
    mem_len = 0;
    for ( i = commandno; i < argc; i++ ) {
        mem_len += strlen(argv[i]) + 2;
        if ( i > commandno ) {
            cmd = (char *)realloc((void *)cmd, mem_len);
        } else { //i == commandno
            cmd = (char *)malloc(mem_len);
        }

        strcat(cmd, argv[i]);
        strcat(cmd, " ");
    }
  if (newline == 0)
        usleep(225000);
    for (i = 0; cmd[i]; i++)
        ioctl (fd, TIOCSTI, cmd+i);
    if (newline == 1)
        ioctl (fd, TIOCSTI, nl);
    close(fd);
    free((void *)cmd);
    exit (0);
}

Copy the above code to some C file (For eg. ttyecho.c). Run the following command in the directory you have created the C file in to compile the code.

make ttyecho

If you named the file as abc.c then the command would be make abc.

Copy this file to the bin directory under your Home Directory. In my case it is /home/pratik/bin. Create the directory if it doesn’t exist. Its a good practice to keep all custom binaries/executables in this bin directory.

Start another terminal or switch to any other open terminal that you wish to control and execute the command tty. You can see a sample output below.

@~$ tty
/dev/pts/5

Now to execute a command on /dev/pts/5, run the following command in the controlling/original terminal.

sudo ttyecho -n /dev/pts/5 ls

You will see that the ls command is executed in /dev/pts/5. The -n option makes ttyecho send a newline after the command, so that the command gets executed and not just inserted. This utility can infact be used to send any data to other terminals For eg, you could open vim in /dev/pts/5 and then run the following command in the controlling terminal to cause vim to exit in /dev/pts/5.

sudo ttyecho -n /dev/pts/5 :q

To avoid using sudo all the time, so that the command is easily scriptable, change the owners/permissions of this executable using the following commands.

sudo chown root:root ttyecho
sudo chmod u+s ttyecho

What we did was change the owner/group to root and set the setuid bit for the executable which will allow you to run the utility with root permissions.

Setting the setuid bit can become a security risk.
The code for the utility can be improved to make it more secure.

About Pratik Sinha

Linux Nerd, Socialist, Atheist, Adventuristic, Nature Lover, Geeky.

21 comments

  1. どもー。
    たまたまブログ読んだよたものでいきなりのコメントでごめんなさい。

    ひっこし運送サービスのあれこれについていろいろポイントがあると思います。ひっこし配送サービスといっても色々な引っ越業者があると思います。例を上げるとよく見かける引っ越し会社だったりそこそこのの引っ越運送サービスもあると思います。また、少量の荷物を扱う会社の引越もあると思います。引越運送業者にはこのように種類が気になりますがどこの引っ越し運送業者を利用するのかはあれこれと自分の引っ越しに応じて考えればいいと思います。例えば家族で大荷物の引越しで、レオパレスなどのような家具付きの部屋へ引っ越をするという人も存在すると思います。そのような場合には誰もが知っているひっこし会社を依頼しなくてもよくて量の少ない荷物専門の業者のひっこしサービスを利用すればいいということになります。

    また単身赴任の引越しの場合には、よく見かける引越運送業者のお徳用パックのようなものが認められますからそちらを利用すればスムーズに独り身の引越が完了します。ということで引越配送サービスは自分の引越しのスタイルや引っ越しの目的に合わせて選択するといいと思いますし、引越業者を選択するときには、はあれこれと比較して選ぶといいのではないかと思いますから、引越し業者を色分けによって選んだり自分の引越ライフスタイルや規模などに合わせてて選択するのがいいかもしれないですね。
    誰もが知っている引っ越業者を選ぶいいところと悪いところについて紹介したいと思います。有名なひっこし運送サービスは名前が通っていて認知度も高く、誰もが知っている引っ越し配送サービスなら安心だと思っている人も多いかもしれませんが、実は誰もが知っているからといって現場次第で乱暴な作業のところもあると思っていて間違いないでしょう。とりあえず有名な引っ越し運送業者のナイスな点は、無料の訪問見積もりがほぼ確実に用意されているので、値段や仕事内容を事前に見積もってもらうことができてグッドですね。また大手引越し運送業者のいいところは安心感や信頼も高いと思いますし、梱包の丁寧さ、傷のトラブルなどの少なさも安心して任せられると思います。またCMで見かける引越し会社はサービスが盛り盛りでエアコンの取り付けや清掃といったバリエーション豊富なサービスを対応しているというのも特長だと思います。

    大手のひっこし会社の悪いところは、なんと言っても価格が高いことではないでしょうか。作業をする人もしっかりしていて、梱包も丁寧で仕事が早いとくれば値段が高いのも問題ないとそういうことだと思いますが著名な引越運送サービスの悪いところは価格がかかるということですね。また安くしてもらえないしてもらえないというのも、有名な引越し運送業者のバッドポイントかもしれません。このように誰もが知っている引越し配送サービスでもナイスな点とネガティブポイントは存在します。

  2. There is one bug in your code.
    “malloc” may return memory not set to zero.
    In this case “strcat” will fail causing “Segmentation fault” error.
    The fix is very easy – use “calloc” instead.
    – cmd = (char *)malloc(mem_len);
    + cmd = (char *)calloc(mem_len, 1);

  3. while true; do echo print this; sleep 5; done;
    this command works standalone but when combined with ttyecho throws an error? Can anyone help me out with that?

    ttyecho -n /dev/pts/2 while true; do echo print this; sleep 5; done; – GIVES ERROR
    bash: syntax error near unexpected token `do’

  4. Can this login to a getty process?
    When I run it with the user name it appears to accept it and show up the request for the password, but then sending the password always returns “Login incorrect”.
    Is a security feature of Linux or is the characters ttyecho ends not compatible with getty?

    1. Linux password entry fields are secured and protected; this utility cannot enter them. Essentially, Linux binds the password entry field directly to the keyboard. So it only registers physical keyboard presses directly from the hardware.

      However, there is another way, I think, to do what you want with the getty terminals. I, for example, use it to help manage my server. It runs non-graphical, and is set up close to my desk: close enough I can see the screen, too far to interact with without getting up. So, I use this program to interact with the physical terminal. The trick is that you have to allocate a new console and attach a login session. This can be achieved with the openvt command.

      # openvt -ls
      will create a new virtual terminal, attach an authenticated bash session, and make that terminal active on the display. You can use ‘chvt ‘ to switch to TTY. ‘deallocvt ‘ removes that TTY.

      Not as straightforward as I would like, but it works.

  5. I do as this page said , but when I run the command, I got the error info :

    [user@host ~/bin]$ sudo ttyecho -n /dev/pts/16 ls
    sudo: ignoring `ttyecho’ found in ‘.’
    Use `sudo ./ttyecho’ if this is the `ttyecho’ you wish to run.

    and then I changed the command , the error info exists also:

    [user@host ~/bin]$ sudo ./ttyecho -n /dev/pts/16 ls
    Sorry, user user is not allowed to execute ‘./ttyecho -n /dev/pts/16 ls’ as root on host.

    why?

  6. Thanks a lot! Saved my day!

    I had a disconnected SSH connection to a server, and in that SSH connection, the mail tool was running with an open overfilled mbox with more than 900.000 mails. Just opening this big mbox took quite a while. Killing the mail process and re-opening that mbox again would’ve been a pain in the a**.

    As I only needed the last ~~ 100 of these mails, I used ttyecho to send “d 1-900000” and “q”, so the mail utility deleted most of the mails and finished nicely, leaving only a few hundred messages for later reading & filtering.

    This worked perfectly, it took just a minute, and that includes the time needed to create ttyecho.c, copy&paste and compiling. 😀

    1. I do as this page said , but when I run the command, I got the error info :

      [user@host ~/bin]$ sudo ttyecho -n /dev/pts/16 ls
      sudo: ignoring `ttyecho’ found in ‘.’
      Use `sudo ./ttyecho’ if this is the `ttyecho’ you wish to run.

      and then I changed the command , the error info exists also:

      [user@host ~/bin]$ sudo ./ttyecho -n /dev/pts/16 ls
      Sorry, user user is not allowed to execute ‘./ttyecho -n /dev/pts/16 ls’ as root on host.

      why?

  7. Wow! Like others said, this tool is truly a gem. So many people states that writing to the stdin of an unrelated process isn’t possible…

  8. Thanks to your need util, I didn’t had to restart a long taking process, that had already finished, but asked me to answer 17272 times “1”. I’m going to include it on all my installations from now on, it is so useful.
    Thanks a lot.

  9. Fantastic! I was poking around all day trying to figure out how to do with tee. Saved me a lot of frustration! Thank you kind sir.

  10. Great tool. I have been searching for a solution to this for a while and always running into dead ends. Nice clean solution to executing commands in other terminals. This is a real gem piece of code.

    Thanks!

  11. that tool just rocks dynamite!

    I’ve written an initramfs generator (http://github.com/r1k0/kigen) supporting dropbear (amongst others) to rescue LUKS systems remotly and I had a very hard time firing up the new init from a remote dropbear console (/dev/ttyp0).

    Thanks to your tool and I can redirect my commands to /dev/console (as if it was local).

    very very nice

Leave a Reply

Your email address will not be published. Required fields are marked *