1

I have written a set of programs with the intent of using a radio transmitter-receiver (NRF24L01) to connect two devices as if they were connected via a serial interface.

Currently, i am able to send bash commands in one direction, lets say from device A to B. My A device is currently an AVR microcontroller.

My B device is a Rapberry Pi. I use the following command to pipe the received text to bash. This allows commands to be sent but not for their output to be sent back to the A device.

./program | bash

I am not sure how to pipe the output from bash back into my program in a way that will not block and prevent the program from reacting to received data. If it is possible to setup a pipe in both directions, I still do not think I can use functions like fgets as they are blocking.

Both devices share the same library for transmit and receive functionality, these transmit and receive functions can be called with an option to make them non-blocking.

fisherdog1
  • 13
  • 2

1 Answers1

1

The easiest approach is probably to have your program use pipe2 three times to create three pipes (for each of stdin, stdout, and stderr). Probably you want them in non-blocking mode. Then fork, and have the child use dup2 to put the pipes into file descriptors 0, 1, and 2. The child then uses one of the exec family to run bash.

The parent can then use select to determine when there is data to be read or space to write.

There are probably libraries or existing implementations you could leverage.

Note #1: pipe2 returns two file descriptors, one for the read side and one for the write side of the pipe. E.g., for bash's stdin, bash needs the read side (to read input) and your program needs the write side (to write bash's input). bash's stdout would be the opposite: bash needs the write side, your program needs the read side.

Note #2: This doesn't give you a full terminal experience; for that you'd need to deal with ptys, which adds a bunch of complexity (and honestly I'd have to look it up). If you want that, I definitely suggest looking for a similar program to start from.

derobert
  • 107,579
  • 20
  • 231
  • 279
  • would you or someone else be willing to elaborate on what functionality will be missing without using ptys? I envision the utility of this code to be accessing remote sensors and non-security-critical equipment. – fisherdog1 Jun 07 '19 at 17:56
  • you know, there are other bi-directional pipes besides ttys. look up socketpair() -- much better than pipe() *even* if you leave one direction unused (because you can turn record mode on with SEQ_PACKET, peek instead of read with recv(MSG_PEEK), use it in non-blocking mode on a per-call basis, authenticate the pair, etc, etc –  Jun 07 '19 at 18:33
  • @fisherdog1 A lot of stuff won't work. E.g., control-C probably won't. Anything trying to do terminal "drawing" or positioning probably won't. – derobert Jun 07 '19 at 18:39
  • @UncleBilly I doubt `SOCK_SEQPACKET` would work; that's not the behavior expected of STDIN/OUT/ERR. `SOCK_STREAM` probably would, though. – derobert Jun 07 '19 at 18:42
  • @fisherdog1 (Basically, to get it all working, you'd be implementing a minimal telnet client & server, just using your radio interface instead of a TCP connection — which may well be a good place to look for code to build on.) – derobert Jun 07 '19 at 18:44
  • why? seqpacket has the same eof semantics like sock_stream, just preserving records. you're probably confusing it with sock_dgram. –  Jun 07 '19 at 18:45
  • @UncleBilly STDIN/STDOUT/STDERR aren't expected to preserve (or even have) record boundaries. E.g., `{ printf 'echo '; printf 'hello world\n'; } | command` shouldn't be two different records, and would likely confuse programs. Also, writes above the fixed maximum length will fail, which probably also will break things. – derobert Jun 07 '19 at 18:48
  • @UncleBilly (It's been a while since I've used them, maybe I'm misremembering — if `read` and `write` work like "normal" and that only applies to `sendmsg`, then it'd work fine, but there'd be no reason to use it over `SOCK_STREAM`). – derobert Jun 07 '19 at 18:55
  • There are a LOT of reasons to use them over sock_stream. Because they're boundary/record preserving, you don't have to run your own ad-hoc messaging protocol/line discipline on top of them. –  Jun 07 '19 at 19:35
  • In your example, with an ordinary pipe, the reader should be prepared to get those 2 writes in 2, 3, 4 or more read()s, anyways. –  Jun 07 '19 at 19:42
  • @UncleBilly but there is nothing record-based here. The best you can hope for is that they work close enough to a stream because that's what bash (etc.) is expecting. And how will write handle something larger than the permissible packet? Does it just fail? Apps will expect a partial write. – derobert Jun 08 '19 at 04:32
  • OP is short in details. They say they want to "send commands" to (and probably receive replies from) some device. It makes perfect sense to use a record preserving channel for that. And the "permissible packet size" on a UDS is quite large and can be made larger. Anyways, for using (stream) unix sockets to implement pipes there's the example of ksh93, and for record-preserving pipes by default there's the example of unix 8-10 and plan9 -- you can research those and see how they work instead of assuming that I'm talking out of my ass. –  Jun 08 '19 at 07:43