Detect if stdin is a terminal or pipe? - c++

When I execute "python" from the terminal with no arguments it brings up the Python interactive shell.
When I execute "cat | python" from the terminal it doesn't launch the interactive mode. Somehow, without getting any input, it has detected that it is connected to a pipe.
How would I do a similar detection in C or C++ or Qt?

Use isatty:
#include <stdio.h>
#include <io.h>
...
if (isatty(fileno(stdin)))
printf( "stdin is a terminal\n" );
else
printf( "stdin is a file or a pipe\n");
(On windows they're prefixed with underscores: _isatty, _fileno)

Summary
For many use cases the POSIX function isatty() is all what it is needed to detect if stdin is connected to a terminal. A minimal example:
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
if (isatty(fileno(stdin)))
puts("stdin is connected to a terminal");
else
puts("stdin is NOT connected to a terminal");
return 0;
}
The following section compares different methods that can be used if different degrees of interactivity have to be tested.
Methods in Detail
There are several methods to detect if a program is running interactively.
Following table shows an overview:
cmd\method ctermid open isatty fstat
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
./test /dev/tty OK YES S_ISCHR
./test < test.cc /dev/tty OK NO S_ISREG
cat test.cc | ./test /dev/tty OK NO S_ISFIFO
echo ./test | at now /dev/tty FAIL NO S_ISREG
The results are from a Ubuntu Linux 11.04 system using the following program:
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
int main() {
char tty[L_ctermid+1];
ctermid(tty);
printf("ID: %s\n", tty);
int fd = open(tty, O_RDONLY);
if (fd < 0) perror("Could not open terminal");
else {
printf("Opened terminal\n");
struct termios term;
int r = tcgetattr(fd, &term);
if (r < 0) perror("Could not get attributes");
else printf("Got attributes\n");
}
if (isatty(fileno(stdin))) printf("Is a terminal\n");
else printf("Is not a terminal\n");
struct stat stats;
int r = fstat(fileno(stdin), &stats);
if (r < 0) perror("fstat failed");
else {
if (S_ISCHR(stats.st_mode)) printf("S_ISCHR\n");
else if (S_ISFIFO(stats.st_mode)) printf("S_ISFIFO\n");
else if (S_ISREG(stats.st_mode)) printf("S_ISREG\n");
else printf("unknown stat mode\n");
}
return 0;
}
Terminal device
If the interactive session needs certain capabilities, you can open the
terminal device and (temporarily) set terminal attributes you need
via tcsetattr().
Python Example
The Python code that decides whether the interpreter runs interactively uses isatty(). The Function PyRun_AnyFileExFlags()
/* Parse input from a file and execute it */
int
PyRun_AnyFileExFlags(FILE *fp, const char *filename, int closeit,
PyCompilerFlags *flags)
{
if (filename == NULL)
filename = "???";
if (Py_FdIsInteractive(fp, filename)) {
int err = PyRun_InteractiveLoopFlags(fp, filename, flags);
calls Py_FdIsInteractive()
/*
* The file descriptor fd is considered ``interactive'' if either
* a) isatty(fd) is TRUE, or
* b) the -i flag was given, and the filename associated with
* the descriptor is NULL or "<stdin>" or "???".
*/
int
Py_FdIsInteractive(FILE *fp, const char *filename)
{
if (isatty((int)fileno(fp)))
return 1;
which calls isatty().
Conclusion
There are different degrees of interactivity. For checking if stdin is connected to a pipe/file or a real terminal isatty() is a natural method to do that.

Probably they are checking the type of file that "stdin" is with fstat, something like this:
struct stat stats;
fstat(0, &stats);
if (S_ISCHR(stats.st_mode)) {
// Looks like a tty, so we're in interactive mode.
} else if (S_ISFIFO(stats.st_mode)) {
// Looks like a pipe, so we're in non-interactive mode.
}
Of course Python is open source, so you can just look at what they do and know for sure:
http://www.python.org/ftp/python/2.6.2/Python-2.6.2.tar.bz2

On Windows you can use GetFileType.
HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
DWORD type = GetFileType(hIn);
switch (type) {
case FILE_TYPE_CHAR:
// it's from a character device, almost certainly the console
case FILE_TYPE_DISK:
// redirected from a file
case FILE_TYPE_PIPE:
// piped from another program, a la "echo hello | myprog"
case FILE_TYPE_UNKNOWN:
// this shouldn't be happening...
}

Call stat() or fstat() and see if S_IFIFO is set in st_mode.

You can call stat(0, &result) and check for !S_ISREG( result.st_mode ). That's Posix, not C/C++, though.

Related

Why does a pipe in write mode outputs something?

While I was playing with pipes in c++ I stumbled accross something rather interesting.
#include <cstdio>
#include <iostream>
#include <string>
int main()
{
FILE *pystream = popen("python","w"); // Calling the python console
fprintf(pystream,"print(2+3)"); // Making it do something
pclose(pystream); // Closing the pipe
return 0;
}
This code outputs 5. but why ? And can the "output" be read or stored somewhere ?
I'm fairly new to C buffers and pipes, so I don't know if I'm using the right terminology.
When you write like this you're effectively writing to the stdin of the process you just started, in this case the python REPL. On Linux the python REPL is getting the expression directly ie it's not being typed in. This is ths system command
read(0, "print(2+3)", 4096) = 10
If you were doing this in the terminal each character is being read in one at a time by the terminal and when it gets carriage return it writes a newline \n ie
read(0, "\r", 1) = 1
write(1, "\n", 1
It then performs the calculation and write the result out
write(1, "5\n", 25
You're by passing the terminal and writing the data directly to the stdin of the python interpreter. If you want to see how this can easily break try this code.
#include <cstdio>
#include <iostream>
#include <string>
int main()
{
FILE *pystream = popen("python","w"); // Calling the python console
fprintf(pystream,"print(2+3)"); // Making it do something
fprintf(pystream,"print(2+3)"); // Making it do something
pclose(pystream); // Closing the pipe
return 0;
}
You will get a syntax error, to make it work the stdin needs to be fed a carriage return or a newline to separate the two lines ie add a carriage return...
fprintf(pystream,"print(2+3)\r");
The standard output of the command you're executing is connected to the standard output of your program, so when the Python writes to its standard output, it appears on the standard output of your process too.
If you had pending output before you ran Python, that won't be flushed and will appear after Python returns. For example,
std::cout << "Hello";
(no endl, no \n in the string) before popen() and
std::cout << " World\n";
after pclose() means that you'll see the Python output before Hello World.
If you want to write to Python and read the results back in your program, you can no longer use popen() and pclose(). Instead, you need to use pipe() twice (one pipe to talk to Python, one pipe to read from Python), and you need to use fork(), exec(), dup2() — probably; dup() otherwise — and close() to make the operations work. You'll be using file descriptors and hence read() and write() system calls in the parent process, too.
Those are all C functions (system calls) more than C++ functions.
This code works:
#include <unistd.h>
#include <cstdio>
#include <cstring>
int main()
{
int p1[2];
int p2[2];
if (pipe(p1) != 0 || pipe(p2) != 0)
return 1;
int pid;
if ((pid = fork()) < 0)
return 1;
if (pid == 0)
{
dup2(p1[0], STDIN_FILENO);
dup2(p2[1], STDOUT_FILENO);
close(p1[0]);
close(p1[1]);
close(p2[0]);
close(p2[1]);
execlp("python", "python", (char *)0);
fprintf(stderr, "failed to exec python\n");
return 1;
}
else
{
close(p1[0]);
close(p2[1]);
const char command[] = "print(2+3)\n";
int len = strlen(command);
if (write(p1[1], command, len) != len)
{
fprintf(stderr, "failed to write command to python\n");
return 1;
}
close(p1[1]);
char buffer[256];
int nbytes;
if ((nbytes = read(p2[0], buffer, sizeof(buffer))) <= 0)
{
fprintf(stderr, "failed to read response from python\n");
return 1;
}
printf("Python said: (%d) [%.*s]\n", nbytes, nbytes, buffer);
close(p2[0]);
printf("Finished\n");
}
return 0;
}
The bad news is that changing this code to write more than one command while synchronously reading a response from Python does not work. Python does not process each line separately as it does when its input is a terminal; it reads all the data before it responds at all. You can work around that with python -i, but then the prompts from Python appear on stderr. So, you can redirect that to /dev/null to lose it:
#include <unistd.h>
#include <fcntl.h>
#include <cstdio>
#include <cstring>
int main()
{
int p1[2];
int p2[2];
if (pipe(p1) != 0 || pipe(p2) != 0)
return 1;
int pid;
if ((pid = fork()) < 0)
return 1;
if (pid == 0)
{
dup2(p1[0], STDIN_FILENO);
dup2(p2[1], STDOUT_FILENO);
close(p1[0]);
close(p1[1]);
close(p2[0]);
close(p2[1]);
int dn = open("/dev/null", O_WRONLY);
if (dn >= 0)
{
dup2(dn, STDERR_FILENO);
close(dn);
}
execlp("python", "python", "-i", (char *)0);
fprintf(stderr, "failed to exec python\n");
return 1;
}
else
{
close(p1[0]);
close(p2[1]);
const char *commands[] =
{
"print(2+3)\n",
"print(3+4)\n",
};
enum { NUM_COMMANDS = sizeof(commands) / sizeof(commands[0]) };
for (int i = 0; i < NUM_COMMANDS; i++)
{
int len = strlen(commands[i]);
if (write(p1[1], commands[i], len) != len)
{
fprintf(stderr, "failed to write command to python\n");
return 1;
}
char buffer[256];
int nbytes;
if ((nbytes = read(p2[0], buffer, sizeof(buffer))) <= 0)
{
fprintf(stderr, "failed to read response from python\n");
return 1;
}
printf("Python said: (%d) [%.*s]\n", nbytes, nbytes, buffer);
}
close(p1[1]);
close(p2[0]);
printf("Finished\n");
}
return 0;
}
Without redirection of stderr:
Python 2.7.10 (default, Oct 23 2015, 19:19:21)
[GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.0.59.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> >>> Python said: (2) [5
]
>>> Python said: (2) [7
]
Finished
With redirection of stderr:
Python said: (2) [5
]
Python said: (2) [7
]
Finished
The disadvantage of losing the standard error output to /dev/null is that you won't get any notice when Python objects to what you send it to execute — the code will hang. Working around that is fun (a third pipe, and using poll() or epoll() or — perish the thought — select() would be one way around the problem).

Communication with Arduino using Linux

This is the first time I'm communicating with Arduino using my computer. I use Ubuntu 14.04. This is the C program for writing to the file. The Arduino shows up ttyACM0.
While compiling using gcc the compiler shows an error saying:
Segmentation fault(core dumped)
How do I rectify this error.
#include<unistd.h>
#include<stdio.h>
int main() {
char data[] = {'f','b','r'}; //Random data we want to send
FILE *file;
file = fopen("/dev/ttyACM0","w"); //Opening device file
int i = 0;
for(i = 0 ; i < 3 ; i++) {
fprintf(file,"%c",data[i]); //Writing to the file
fprintf(file,"%c",','); //To separate digits
sleep(1);
}
fclose(file);
}
Pardon my ignorance. I tried researching on it. Couldn't make it work. Thanks in advance for your help.
You're getting a NULL return from the fopen() that NULL is being passed to fprintf() which is expecting a valid FILE* and messing up causing the SEGV.
If you use fopen you should check what it returns so you can give the user a something more useful than "segmentation fault".
The probable cause of the fopen() failure is you don't have permission to play with the serial port.
Normally you need the group dialout to be able to access the serial port.
As root do:
usermod -a -G dialoutyourusername
Then log out and back in so you get the new group.
Consider using minicom or microcom (on any of the several other serial terminal programs) to access the serial port instead of writing your own.
I also suggest you have the Arduino send a hello message when it boots up so you can be sure you have the right baud rate etc...
You did not put any success check on the return value of fopen("/dev/ttyACM0","w");. In case fopen() fails, using file further is undefined behavior, causing segmentation fault. Do something like
file = fopen("/dev/ttyACM0","w"); //Opening device file
if (file)
{
//do something with file
}
else
return 0;
Also, add a return 0 before ending main().
// the following code:
// compiles cleanly
// performs appropriate error checking
// has proper return statement
#include <unistd.h> // sleep()
#include <stdio.h> // fopen(), fclose(), fprintf(), perror()
#include <stdlib.h> // exit() and EXIT_FAILURE
int main()
{
char data[] = {'f','b','r'}; //Random data we want to send
FILE *file;
if( NULL == (file = fopen("/dev/ttyACM0","w") ) ) //Opening device file
{ // then fopen failed
perror("fopen failed for ttyACM0" );
exit( EXIT_FAILURE );
}
// implied else, fopen successful
int i = 0;
for(i = 0 ; i < 3 ; i++)
{
if( 0 >= fprintf(file,"%c",data[i]) ) //Writing to the file
{ // fprintf failed
perror("fprintf data failed" );
exit( EXIT_FAILURE );
}
// implied else, fprintf successful for data
if( 0 >= fprintf(file,"%c",',') ) //To separate digits
{ // then, fprintf failed
perror( "fprintf for comma failed");
exit( EXIT_FAILURE );
}
// implied else, fprintf successful for comma
sleep(1);
} // end for
fclose(file);
return(0);
} // end function: main
On failure fopen returns NULL, so you are potentially dereferencing a NULL pointer, the correct way of doing that, is checking the result of fopen. I would however suggest low level IO for this kind of thing something like
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
int main()
{
char data[] = {'f','b','r'}; //Random data we want to send
int fd;
int i;
fd = open("/dev/ttyACM0", O_WRONLY); //Opening device file
if (fd == -1)
{
perror("cannot open /dev/ttyACM0");
return -1;
}
for(i = 0 ; i < 3 ; i++)
{
write(fd, &(data[i]), 1);
write(fd, ",", 1);
sleep(1);
}
close(fd);
return 0;
}
on error open returns a special value -1 so you should abort writing to it.
I'm pretty sure in your case there will be a permission denied error, since normally the /dev/tty* belong to group dialout and they have group write permission by default, but since probably your user doesn't belong to that group you don't have write access to /dev/ttyACM0.

How to read the failure log message displayed when a system call failed in C++?

I have a C++ code that calls a test. I am doing a system call to execute this test. When this test fails, it will display something like this " ERROR: One or more devices of following component type(s) could not be discovered:"
I have a C++ code that runs on Linux redhat and it is capable of detecting if the system call pass or failed. But it can not capture the error message (ERROR: One or more devices of following component type(s) could not be discovered:) and append into the log file or print it.
Can someone please tell me how to capture the error message (ERROR: One or more devices of following component type(s) could not be discovered:)?
PS: I am an intern, any help would be really nice.
#include<iostream.h>
int main ()
{
int i;
if (system(NULL))
puts ("Ok");
else
exit (1);
i=system("hpsp --discover -verbose --user Admin --oapasswd password");
printf ("The value returned was: %d.\n",i);
return false;
}
Instead of using system() use popen(). This will open a pipe capturing the standard output of the test program so that your program can read it via the pipe.
Example EDITED:
#define _BSD_SOURCE 1
#define BUFFSIZE 400
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *cmd = "hpsp --discover -verbose --user Admin --oapasswd password";
char buf[BUFFSIZE];
char* searchResult;
int testPassed = 0;
FILE *ptr;
if ((ptr = popen(cmd, "r")) != NULL)
while (fgets(buf, BUFFSIZE, ptr) != NULL)
{
if ((searchResult = strstr(buf, "The test passed")) != NULL )
{
testPassed = 1;
break;
}
}
if (testPassed)
printf("yea!!\n");
else
printf("boo!!\n");
pclose(ptr);
return 0;
}
You can use dup and dup2 to backup/store the stderr file descriptor to redirect to your log file. Well, I'm guessing that errors go to stderr anyways.
Here's an example if you just want to write to a log file.
//open log file, choose whatever flags you need
int logfd = open("whateveryourlogfileis", O_APPEND);
//back up stderr file descriptor
int stderr_copy = dup(STDERR_FILENO);
//redirect stderr to your opened log file
dup2(logfd, STDERR_FILENO);
//close the original file descriptor for the log file
close(logfd);
//system call here
//restore stderr
dup2(stderr_copy, STDERR_FILENO);
//close stderr copy
close(stderr_copy);
Note: dup2 closes the target file descriptor before dup2ing to it. dup just duplicates the file descriptor and returns to you the new file descriptor.

trouble pipeline three commands "dmesg|sort|more" c++

I have successfully piped the output of one command into the input of another and then show the output of the second command to the screen.
I want to do this with three successive commands. (actually eventually I want to do it with N commands passed into the program at run time.
This is my attempt at pipelining three commands together.
UPDATED: i updated my question to reflect my latest try.
#include <string.h>
#include <fstream>
#include <iostream>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
using namespace std;
int main(int argc, char * argv[])
{
pid_t pid;
int pfd[2];
char* prgname = NULL;
if(pipe(pfd) == -1)
{
perror("error on pipe call");
return(1);
}
for(int j = 0;j<numberOfCommands;j++)
{
cout<<commands[j]<<"_"<<endl;
}
pid = fork();
if(pid == 0){//child process
close(pfd[0]); //close read end of pipe
dup2(pfd[1],1);//connect the pipes
close(pfd[1]);//close extra file descriptors
prgname = (char*)"dmesg"; //commands[0];//first command
execlp(prgname, prgname, 0);//Load the program
}
else
{
int pfd2[2];
if(pipe(pfd2) == -1)
{
perror("error on pipe call 2");
return(1);
}
pid = fork();
if(pid == 0)//child
{
close(pfd[1]);
dup2(pfd[0],0);
close(pfd[0]);
close(pfd2[0]);
dup2(pfd2[1],1);
close(pfd2[1]);
prgname = (char*)"sort";
execlp(prgname,prgname,0);
}
else
{
close(pfd2[1]); //close the write end of the pipe
dup2(pfd2[0],0);//connect the pipes
close(pfd2[0]); //close extra file descriptor
prgname = (char*)"more"; //commands[1];//now run the second command
execlp(prgname, prgname, 0);//Load the program
}
}
return 0;
}
i have hard coded all values for simplicity.
The program displays what should be the output of "dmesg|more" but does not do the sort part and then freezes. I see the begging of dmesg and more in the bottom left but i can not view any more.
Any ideas?
pipe(2) only provides 2 file descriptors for 1 pipe. The 3rd file descriptor (pfd[2]) is junk and is never getting initialized. If you want to create a pipeline with 3 commands in it, you need to call pipe() twice to get two pipes: one for connecting the first and second processes, and one for connecting the second and third processes.

bi-directional communication using socketpair: hangs reading output from child process

I'm trying to use a socketpair to have a parent process provide input to a child process that execs a different program (e.g., grep) and then read the resulting output. The program hangs in the while loop that reads the output from the program that the child execs.. The child dupes stdin and stdout on to its end of the socketpair and the parent and the child both close their unused end of the pair.
Interestingly, if the child execs a program that I wrote (OK, I ripped it off from Stevens Advanced Programming in the Unix Environment) everything works as expected. However, if the child execs grep (or some other standard program) the parent invariably hangs in trying to read the output. I can't tell if the input is not reaching grep or if the grep cannot determine the end of the input or if the output is somehow being lost.
Here's the code:
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include <cstdio>
#include <cerrno>
#include <iostream>
using namespace std;
void
sigpipe_handler(int sig, siginfo_t *siginfo, void * context) {
cout << "caught SIGPIPE\n";
pid_t pid;
if (errno == EPIPE) {
throw "SIGPIPE caught";
}
}
int main(int argc, char** argv) {
struct sigaction sa;
memset(&sa, '\0', sizeof(struct sigaction));
sa.sa_sigaction = sigpipe_handler;
sa.sa_flags = SA_SIGINFO | SA_RESTART;
sigaction(SIGPIPE, &sa, NULL);
int sp[2];
socketpair(PF_UNIX, SOCK_STREAM, AF_UNIX, sp);
pid_t childPid = fork();
if (childPid == 0) {
close(sp[0]);
if (dup2(sp[1], STDIN_FILENO) != STDIN_FILENO) throw "dup2 error to stdin";
if (dup2(sp[1], STDOUT_FILENO) != STDOUT_FILENO) throw "dup2 error to stdout";
execl("/bin/grep", "grep", "-n", "namespace", (char*)NULL);
} else {
close(sp[1]);
char line[80];
int n;
try {
while (fgets(line, 80, stdin) != NULL) {
n = strlen(line);
if (write(sp[0], line, n) != n) {
throw "write error to pipe";
}
if ((n=read(sp[0], line, 80)) < 0) { // hangs here
throw "read error from pipe";
}
if (n ==0) {
throw "child closed pipe";
break;
}
line[n] = 0;
if (fputs(line, stdout) == EOF) {
throw "puts error";
}
if (ferror(stdin)) {
throw "fgets error on stdin";
}
exit(0);
}
} catch (const char* e) {
cout << e << endl;
}
int status;
waitpid(childPid, &status, 0);
}
}
Your code hangs as grep's output may be less than 80 bytes and you are issuing a blocking read on sp[0]. The proper way of doing this is by marking both sockets as non-blocking and selecting() over both of them.
You also forgot to close(sp[0]) before you wait(), which will leave your child process waiting for input.
You cannot achieve deadlock-free bidirectional communication with a subprocess using UNIX pipes or socketpairs, because you don't have control over buffering in the subprocess.
It just so happens that cat can be trusted to read one line and immediately print it, regardless of whether its standard output is a tty, a pipe or a socket. This is not the case with grep (and actually most programs using stdio), which will buffer output in-process (in the stdio buffers) and defer the write() call until either the buffer is full or the stdio stream is closed (typically because grep is about to exit after having seen EOF on input).
You can trick line-oriented programs (including grep) into not buffering by using a pseudo-tty instead; take a look at libexpect(3). But in the general case, you would have to re-run a different subprocess for each message, which allows to use EOF to signal the end of each message and cause whatever buffers in the command (or pipeline of commands) to be flushed.
See more info about this problem in the perlipc man page (it's for bi-directional pipes in Perl but the buffering considerations apply regardless of the language used for the main program).
It works fine with cat, so the problem is with grep. May be grep output behave differently when connected to something else than a terminal. Or it is not detecting the pattern for some reason.