As per the manual of popen
, which says that[emphasis mine]:
The popen() function opens a process by creating a pipe, forking, and invoking the shell. Since a pipe is by definition unidirectional, the type argument may specify only reading or writing, not both; the resulting stream is correspondingly read-only or write-only.
As per the statements above, there is no way to both write and read to the file stream returned by popen
. So I take it for granted that the stdin
of current process would not influence the behaviour of the shell command which is passed as parameter for popen()
.
But it's really out of my expectation when running the program below.
Here is the code snippet code:
//filename pipe_cmd.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
int ret = 0;
FILE* file = popen("ssh -t [email protected] bash -c 'top' ", "r");
int fd = fileno(file);
char buffer[1024*100];
if(NULL != file)
{
while (true) {
memset(buffer, '\0', sizeof(buffer));
int ret_read = read(fd, buffer, sizeof(buffer));
if (ret_read > 0) {
printf("%s", buffer);
} else if (0 == ret_read) {
ret = 0;
break;
} else {
ret = -1;
break;
}
}
}
return ret;
}
The code snippet is compiled by gcc pipe_cmd.c -o pipe_cmd
.
When program named pipe_cmd
is running, I can see the all the information about the processes on the remote computer, which is as expected.
But when I hit h
key, I can see such help message on my local terminal, which is really out of my expectation. Here is what I see:
Help for Interactive Commands - procps-ng version 3.3.9
Window 1:Def: Cumulative mode Off. System: Delay 0.1 secs; Secure mode Off.
Z,B,E,e Global: 'Z' colors; 'B' bold; 'E'/'e' summary/task memory scale
l,t,m Toggle Summary: 'l' load avg; 't' task/cpu stats; 'm' memory info
0,1,2,3,I Toggle: '0' zeros; '1/2/3' cpus or numa node views; 'I' Irix mode
f,F,X Fields: 'f'/'F' add/remove/order/sort; 'X' increase fixed-width
L,&,<,> . Locate: 'L'/'&' find/again; Move sort column: '<'/'>' left/right
R,H,V,J . Toggle: 'R' Sort; 'H' Threads; 'V' Forest view; 'J' Num justify
c,i,S,j . Toggle: 'c' Cmd name/line; 'i' Idle; 'S' Time; 'j' Str justify
x,y . Toggle highlights: 'x' sort field; 'y' running tasks
z,b . Toggle: 'z' color/mono; 'b' bold/reverse (only if 'x' or 'y')
u,U,o,O . Filter by: 'u'/'U' effective/any user; 'o'/'O' other criteria
n,#,^O . Set: 'n'/'#' max tasks displayed; Show: Ctrl 'O' other filter(s)
C,... . Toggle scroll coordinates msg for: up,down,left,right,home,end
k,r Manipulate tasks: 'k' kill; 'r' renice
d or s Set update interval
W,Y Write configuration file 'W'; Inspect other output 'Y'
q Quit
( commands shown with '.' require a visible task display window )
Press 'h' or '?' for help with Windows,
What a surprise! The aforementioned behaviour looks like the top
command is directly called in my local bash, but it's really called by popen
.
I am really confused now. Could somebody shed some light on this matter?
Note:
The IP address(i.e. 192.168.1.51
) in the code snippet is my local computer's IP. But I don't think it's the reason why the program should work in this way.
I will try to run this program on another computer right now.
UPDATED:
Some phenomena when this program runs on another computer whose IP address is different without any modification for the aforementioned code snippet.
How to solve this problem? I don't want the stdin
of current process influence the behaviour of the shell command. One method that I know is to use pipe
& fork
and close the stdin
. Any better way to achieve this goal with popen
?
CodePudding user response:
From the same manual:
reading from the stream reads the command's standard output, and the command's standard input is the same as that of the process that called popen()
The command you are executing is ssh
. It tries to read from the same terminal you are running your program from. This creates a race, but since the program doesn't read anything, ssh
always wins. Then ssh
passes your input to the remote program which interprets it.
You can redirect the standard input of the command being executed with normal shell syntax
FILE* file = popen("... < /dev/null", "r");
but this will not work with top
and other interactive commands that expect their standard input to be a terminal.