-1

I have a c++ program:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fstream>
#include <iostream>
#include <fcntl.h>
using namespace std;



int main (int argc, const char * argv[]) {
//    system("script /dev/null");
    FILE *pout;
    pout = popen("screen tty.MobileRobot-RNI-SPP", "w");
    fprintf(pout,"hello");


    return 0;
}

The issue is that it then outputs "Must be connected to a terminal."

Which the command then hangs. Uncommenting that system() call doesn't cause anything to happen. I'm not sure if I'm doing it correctly. Any ideas? Thanks

StarckOverflar
  • 1,577
  • 2
  • 10
  • 13
  • You need to connect the `stdin` pipe of that process to an actual terminal in the general case, though there might be a screen-specific way of doing this. – wizzwizz4 Nov 19 '18 at 18:14
  • 1
    Invoking `screen` within another program seems unusual. Can you explain what you're actually trying to do? – Brian Bi Nov 19 '18 at 18:28
  • This seems like a [XY problem](http://xyproblem.info). What is the *actual* problem you are trying to solve? – Jesper Juhl Nov 19 '18 at 18:43
  • Unrelated, but you say "I have a c++ program".. To *me* it seems more like you have a *C* program. There's nothing C++ about the code you posted (except the `using namespace` bit, but that's irrelevant). – Jesper Juhl Nov 19 '18 at 18:45
  • @Brian I need to send data out of a tty port but for some inexplicable reason, echo doesn't work correctly from both terminal and c++. Furthermore, screen works. – StarckOverflar Nov 19 '18 at 19:20
  • @JesperJuhl I need to send data to a tty device, but echo from terminal does not work. ostream dev() doesn't work in c++. Only screen works and I'm very lost. – StarckOverflar Nov 19 '18 at 19:21

1 Answers1

-1

You need to connect the stdin pipe of your subprocess to an actual terminal... or, at least, a pseudoterminal. Assuming you're on a POSIX system (you are, judging by your tags) you can use posix_openpt and ptsname to get these.

Pseudocode (NOT THREAD-SAFE):

const int flags = O_RDWR;
int pseudouser = posix_openpt(flags);
int pseudoprog = open(ptsname(pseudouser), flags);

if (fork()) {
    execv magic with pseudoprog;
    exit(0);
}

FILE *pout = fdopen(pseudouser, "r+");
do stuff with pout

For the execv magic, see this lovely blog post. Basically, it does some magic to cause the executed process to use certain file descriptors, which can then be read by the main program.

Alternatively, you can do a little hack (still NOT THREAD-SAFE):

const int flags = O_RDWR;
int pseudouser = posix_openpt(flags);
char *pseudoname = ptsname(pseudouser);
FILE *pout = fdopen(pseudouser, "r+");

free(popen(pseudo_sprintf("screen tty.MobileRobot-RNI-SPP >\"%s\" 2>\"%s\" <\"%s\"", pseudoname, pseudoname, pseudoname)));

do stuff with pout

Replace pseudo_sprintf with a real snprintf call and proper buffering.

wizzwizz4
  • 6,140
  • 2
  • 26
  • 62
  • I don't understand. What does stdin have to do with this? – StarckOverflar Nov 19 '18 at 19:58
  • @Bob Unix is very file-centric. Some files are pseudoterminals. `screen` was complaining that it was connected to a pipe instead of a pseudoterminal, so you can connect it to a pseudoterminal instead. – wizzwizz4 Nov 19 '18 at 19:59
  • @Downvoter Sorry, why? I can't improve if you don't at least tell me what's wrong with this _perfectly good_ answer. – wizzwizz4 Nov 19 '18 at 20:00
  • It wasn't me. I got downvoted too. But I'm confused on how you know which tty device to be opened using posix_openpt? Shouldn't you specify the device name? My bluetooth module is called tty.MobileRobot.. for example – StarckOverflar Nov 19 '18 at 20:05
  • @Bob That function creates a new virtual terminal pair. – wizzwizz4 Nov 19 '18 at 20:09
  • I'm not too familiar with this stuff. I still don't understand how a virtual terminal pair would indicate that I specifically want my particular port. For example, what if I have multiple ttys available? – StarckOverflar Nov 19 '18 at 20:11
  • @Bob It literally invents a new virtual terminal, and uses that. – wizzwizz4 Nov 19 '18 at 20:22
  • Then how can I get that to correspond to the bluetooth device that I want? – StarckOverflar Nov 19 '18 at 20:29
  • @Bob You've got a case of the [XY problem](https://meta.stackexchange.com/q/66377/308065). Please [ask] a new question explaining what you actually want to do. – wizzwizz4 Nov 19 '18 at 21:26
  • For one, I did ask my real issue and no one has replied. As of now, I simply want to open a screen connection from C++ and send data to a particular bluetooth device. I assume your answer is answering my question, but I'm confused on how it works. I don't see the relevance to the XY problem – StarckOverflar Nov 19 '18 at 22:09
  • @Bob You haven't asked "how can I open a `screen` connection and send data to a particular bluetooth device". You've asked "How do I use `popen` to open "screen" in C++?". – wizzwizz4 Nov 20 '18 at 07:02
  • Ok then my mistake. So is there a better way to do it in c++ than using popen? Would greatly appreciate it – StarckOverflar Nov 20 '18 at 15:52
  • @Bob Well... You could look at the GNU screen source code and see how they implemented it. (Try searching for the error message you get, and work backwards to figure out what code path it's not going down, what code path it should be going down, and what it does to make it work when nothing else does.) – wizzwizz4 Nov 20 '18 at 17:43
  • I'm sure that would be the best solution, but I don't have the time and definitely not the experience to be able to do that. – StarckOverflar Nov 21 '18 at 04:47