1

I'm recently writing a piece of code to access an external server using SSH and then communicating with interactive shell-like application to which I'm directly connecting through it.

I'm using embedded Linux with only basic libraries available, with no possibility to use any additional software nor library. Also, I have to do it via C/C++ code inside the application. So I've decided to use pipes and read(), write() system calls, and I would rather stick to that.

I've written some code to better understand and test the concept. But it doesn't work as expected. I've used a snippet from here. It seems to work fine, but then the loop in main behaves not as expected

#include <string.h>
#include <signal.h>
#include <unistd.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>


static bool
start_subprocess(char *const command[], int *pid, int *infd, int *outfd)
{
    int p1[2], p2[2];

    if (!pid || !infd || !outfd)
        return false;

    if (pipe(p1) == -1)
        goto err_pipe1;
    if (pipe(p2) == -1)
        goto err_pipe2;
    if ((*pid = fork()) == -1)
        goto err_fork;

    if (*pid) {
        /* Parent process. */
        *infd = p1[1];
        *outfd = p2[0];
        close(p1[0]);
        close(p2[1]);
        return true;
    } else {
        /* Child process. */
        dup2(p1[0], STDIN_FILENO);
        dup2(p2[1], STDOUT_FILENO);
        close(p1[0]);
        close(p1[1]);
        close(p2[0]);
        close(p2[1]);
        execvp(*command, command);
        /* Error occured. */
        fprintf(stderr, "error running %s: %s", *command, strerror(errno));
        abort();
    }

err_fork:
    close(p2[1]);
    close(p2[0]);
err_pipe2:
    close(p1[1]);
    close(p1[0]);
err_pipe1:
    return false;
}

int main() {
    char *cmd[4];
    cmd[0] = "/usr/bin/ssh";
    cmd[1] = "-tt";
    cmd[2] = "user@localhost";
    cmd[3] = NULL;

    char buf[65535];
    char msg[65535];

    int pid, infd, outfd;
    start_subprocess(cmd, &pid, &infd, &outfd);


    printf ("Started app %s as %d\n\n", *cmd, pid);

    while(1) {
        read(outfd, buf, 65535);
        printf(">>> %s\n", buf);
        printf("<<< ");
        scanf("%s", msg);
        if(strcmp(msg, "exit") == 0) break;

        write(infd, msg, strlen(msg));
    }

    return 0;
}

I've experimeted with various SSH -t settings and it seems to somehow work with -tt option enabled (As I understand it forces pseudoterminal), without it I'm getting

Pseudo-terminal will not be allocated because stdin is not a terminal.

So I assume -tt is correct here. But the beheviour is strange. I wanted to connect through SSH, then issue ls command and see the output, which should be similar to normal SSH :

user@xubuntuLTS ~/dev/cpp/pipes$ ssh localhost
>>>> WELCOME TO SSH SERVER <<<<
Last login: Thu Jan  3 22:34:35 2019 from 127.0.0.1
user@xubuntuLTS ~$ ls
Desktop  dev  Documents  Downloads  Music  Pictures  Public  Templates  TEST_FILE  Videos
user@xubuntuLTS ~$ 

But insted, my application works like this:

user@xubuntuLTS ~/dev/cpp/pipes$ ./a.out 
Started app /usr/bin/ssh as 18393

>>>> WELCOME TO SSH SERVER <<<<
>>> Last login: Thu Jan  3 22:35:28 2019 from 127.0.0.1

<<< ls
>>> user@xubuntuLTS ~$ 
<<< 
ls
>>> ls0;user@xubuntuLTS: ~user@xubuntuLTS ~$ 
<<< ls
>>> ls0;user@xubuntuLTS: ~user@xubuntuLTS ~$ 

Can you hint me what is wrong in my code? I want to read exactly the same output as I see during "normal" SSH session from terminal, possibly having "appending" output during each read() call, so I can easily perform some automated task with this type of interactive communication. Please note that using standard terminal here is just an example, in the real world solution I'm connecting to some kind of command line interface program directly by logging through SSH, without actual access to shell.

I'm pretty sure that there is something wrong with correct usage of write() and read() here, but I'm not an expert in that matter.

Barmar
  • 741,623
  • 53
  • 500
  • 612
TigerInALoop
  • 225
  • 1
  • 2
  • 8
  • 1
    Your code assumes that a single `read()` will return all the output from the server. That's generally not true with network connections, so you're getting out of sync. – Barmar Jan 03 '19 at 22:09
  • You should use two threads. One thread reads from the pipe and writes to stdout, the other thread reads from stdin and writes to the pipe. – Barmar Jan 03 '19 at 22:10
  • 1
    You can also do it in a single thread using `select()` or `epoll()` to process both stdin and the pipe concurrently. – Barmar Jan 03 '19 at 22:12
  • I think there are a couple good questions here, but I voted to close because you're literally asking *can you hint me what is wrong in my code?*, which is off topic for stack overflow. I'd recommend you break this down into two steps: (1) write basic code to *read()* and *write()* from a network connection, and then (2) go back and connect it with your pipes and threads. This will not only help you identify the problems with your code, but will help you ask more concise questions. – cegfault Jan 03 '19 at 22:34
  • You also ignore the return value of `read`, so you have no idea how many bytes you read. How are you expecting `printf` to know how many bytes to print? Your code sometimes proxies in one direction and sometimes in the other and has no reliable way to know when and whether it's going in the right direction. Either use `select`/`poll` and non-blocking sockets, use another process, or use threads. – David Schwartz Jan 03 '19 at 22:35
  • You almost certainly have one of the SSH libraries already installed; either `libssh` or `libssh2`. What you are trying to do is probably easier to do using the library. – Nominal Animal Jan 04 '19 at 19:08

0 Answers0