Change stdin/stdout during runtime

marjonz
Posts: 12
Joined: Thu Aug 09, 2018 7:33 pm

Change stdin/stdout during runtime

Postby marjonz » Thu Aug 09, 2018 9:07 pm

Hi,

First of all, I have tried reading the documentation:

"To change the default stdin, stdout, stderr streams for new tasks, modify _GLOBAL_REENT->_stdin
(_stdout, _stderr) before creating the task."

but I think the information there is slightly different from what I am trying to achieve.

I have two separate tasks, telnet (running in one task) and a "console" (running in a second task) piping things to a UART port.
The console task is running fine, piping things out to its "_GLOBAL_REENT->stdin" instance.
The telnet task is also running fine taking input from the telnet peer and piping output using "telnet_send" (from libtelnet.c, https://github.com/seanmiddleditch/libtelnet).
Now I want to somehow redirect the console's stdin/stdout to the telnet session, when I receive a certain keypress combination.
I have done so far:
    1. handle the keypress combination I want
    2. Add in the corresponding function handlers for when an input stream is coming from the telnet session
    3. Add in a function (that runs in the main task of the telnet task) to read from a buffer and pipe the output using "telnet_send"

Need to do:
    1. Get the "instance" of the stdout of the console, and somehow get it's output and pipe it to a buffer (see #3 above).
    2. Get the "instance" of the stdin of the console, and somehow get the telnet input to pipe it to that stdin.

Is there a way to change a task's stdin/stdout during runtime (after the task has been created)?
And is there a way to restore it?

Marjon

ESP_igrr
Posts: 1143
Joined: Tue Dec 01, 2015 8:37 am

Re: Change stdin/stdout during runtime

Postby ESP_igrr » Fri Aug 10, 2018 5:16 pm

To change stdin/stdout of an already created task, you can simply assign to stdin and stdout variables from the task itself (they are FILE pointers). To restore, simply set old values. Note that stdin and stdout are per-task, so changing them from one task does not affect other tasks.

To achieve redirection, check "funopen" function, it allows redirecting a stream to arbitrary read/write functions, which you can supply.

marjonz
Posts: 12
Joined: Thu Aug 09, 2018 7:33 pm

Re: Change stdin/stdout during runtime

Postby marjonz » Sun Aug 12, 2018 9:12 pm

Thanks for that, any documentation on the API funopen?
This was as far a documentation as I got:
FILE *_EXFUN(funopen,(const _PTR __cookie,
int (*__readfn)(_PTR __cookie, char *__buf,
_READ_WRITE_BUFSIZE_TYPE __n),
int (*__writefn)(_PTR __cookie, const char *__buf,
_READ_WRITE_BUFSIZE_TYPE __n),
fpos_t (*__seekfn)(_PTR __cookie, fpos_t __off, int __whence),
int (*__closefn)(_PTR __cookie)));


Looks like I have to register my own implementations for the read/write/seek/close functions? Correct?

ESP_igrr
Posts: 1143
Joined: Tue Dec 01, 2015 8:37 am

Re: Change stdin/stdout during runtime

Postby ESP_igrr » Mon Aug 13, 2018 8:10 pm

funopen is a BSD extension, you can refer to the following manpage: https://www.freebsd.org/cgi/man.cgi?que ... ease-ports.

Can you clarify if you still need UART to be used for input/output when this redirection takes place?

i.e. is it

a) UART <-> telnet (no processing in between)

or

b) Console component <-> telnet (UART not involved)

marjonz
Posts: 12
Joined: Thu Aug 09, 2018 7:33 pm

Re: Change stdin/stdout during runtime

Postby marjonz » Mon Aug 13, 2018 10:14 pm

I ended up creating a separate ring buffer inside my telnet module, and implemented my own tx_char/rx_char functions to register with UART that required the redirection. I then registered these new functions into the uart.c driver as the new tx and rx functions for the UART to use when doing the character reads/transmits.

I didn't end up touching the stdin/stdout, as I ran out of patience (I've tried as least three different ways of trying to redirect that).

I think this thread could be closed.

marjonz
Posts: 12
Joined: Thu Aug 09, 2018 7:33 pm

Re: Change stdin/stdout during runtime

Postby marjonz » Tue Aug 14, 2018 9:17 pm

Scratch that, actually, I can send the console's output to the telnet session. But I could not get the telnet input to pass through to the console's input. Also, once the console output stream was piped to the telnet session, the telnet seemed to be getting random (unexpected) inputs.

marjonz
Posts: 12
Joined: Thu Aug 09, 2018 7:33 pm

Re: Change stdin/stdout during runtime

Postby marjonz » Wed Aug 15, 2018 3:19 am

Hi igrr,

My telnet output (piped from the console unit) is currently like this:

Code: Select all

hash>
hash>
 [6nlhash> p
hash>


It looks like what I am trying to do might not be compatible based on this link: https://dl.espressif.com/doc/esp-idf/latest/api-guides/console.html

I am also seeing a loop between the stdin/stdout. Since I have piped the output of the console to the telnet session, if I press "enter" (carriage return + new line), it goes into the console session (as expected), gets echoed to the telnet session, gets fed back (since now my stdin is from the telnet session) to the console, then looped again forever.

Any suggestions how to fix?

marjonz
Posts: 12
Joined: Thu Aug 09, 2018 7:33 pm

Re: Change stdin/stdout during runtime

Postby marjonz » Mon Aug 20, 2018 3:53 am

Solution, change the linenoise call to the blocking fgetc() to a non-blocking (with timeout) fread.

The fgetc() will persist even if I flushed and closed (and even tried relocating stdin) stdin, until an input is received from it.

donvar
Posts: 1
Joined: Tue Sep 25, 2018 2:41 pm

Re: Change stdin/stdout during runtime

Postby donvar » Tue Sep 25, 2018 2:52 pm

I am working on map console on telnet follows you way. but I get stuck in how to redirct stdin/sdout by using funopen/fropen/fwopen. I am appreciated that if you can provide a code sample for calling these functions.

marjonz
Posts: 12
Joined: Thu Aug 09, 2018 7:33 pm

Re: Change stdin/stdout during runtime

Postby marjonz » Fri Sep 28, 2018 1:32 am

Here you go:

Code: Select all

const char* uart_dev = "/dev/uart/0"
static new_transmit_fn();

/*! Switch the standard input/output streams to the
 * implementation in telnet.
 */
void switch_file_stream_to_telnet(void)
{
    fflush(stdin);
    fclose(stdin);

    // Tell VFS to telnet's virtual tx/rx functions
    esp_vfs_dev_uart_use_virtual_channel_driver( UART_1, new_transmit_fn, NULL);
   _GLOBAL_REENT->_stdin = fopen(uart_dev , "r");
}


If you need to implement your own receive function, replace the NULL with your own receive function implementation.

Who is online

Users browsing this forum: Google [Bot] and 22 guests