getting the return code of forked child process (ftp)

Hi,

From within my C++ program, I fork a child process and execl an ftp session (solaris), like this :

std::string szStartCmd = "ftp -i -n -v 192.168.149.31";

int nExecRes = execl("/bin/sh", "sh", "-c", szStartCmd.c_str(), (char *)0);

I use 2 pipes to communicate between my parent process and the ftp. But I'm not sure if it's possible to catch the output of the solaris ftp in a pipe, so that I can use it in my parent process. Until now I've only used uni-directional communication between parent and child process.
Getting commands from my parent to the ftp process works fine, but the ftp output is just printed on the screen. I would like to "catch" it in my parent process. Does anyone know if this is possible ?

Call waitpid() using the pid of the child.

waitpid() will return an exit status that can be decoded by macros defined in sys/wait.h like WIFEXITED. Make sure your child returns an exit code from what you are trying to check - ftp. This means your child will have to learn about the return codes in RFC 959 and decide which ones are bad and which ones are okay. The ftp process itself doesn't usually return errors the way you would think, it displays return codes to stdout. And there are quite a few of them.

As jim mentioned, since ftp displays return codes on the stdout, you might want to think about using popen instead of execl to read the return codes straight off the child's stdout.

Hello,

Thank you for the answers.
So, now I'm sure it's not something "impossible" that I'm trying to do.

But I'm not there yet. I've copied what I've got so far below.
With this piece of code, I can send ftp commands and get the response on standard output. But how do I catch it now ?

I thought read(fd[0],receive,strlen(receive)); would do this.
Maybe something is wrong with the close and dup'ing in the child process ?

I haven't tried the popen yet, because I have the idea that I'm very close with what I have now.

Does anyone see what's still wrong ?

   int fd[2], nStatus;
pid_t pid;

pipe\(&fd[0]\);	//create pipe
if \(\(pid=fork\(\)\) != 0\) \{
	// parent
	close\(fd[0]\);
	//close\(STD_OUTPUT\);
	//dup\(fd[1]\);	// sets stdout to this pipe end 
	//close\(fd[1]\);  // don't need fd anymore 
	
	std::cout << "I'm the parent. I'm a child process of : " << getppid\(\) << ", my pid is " << getpid\(\) << std::endl;		

\} else \{
	// child 
	close\(fd[1]\);
	close\(STD_INPUT\);
	dup\(fd[0]\);	// replace stdin 
	close\(fd[0]\);
	
	std::cout << "I'm the child. I'm a child process of : " << getppid\(\) << ", my pid is " << getpid\(\) << std::endl;		
	
	std::string szStartCmd = "ftp -i -n -v 192.168.149.31";	
		
	int nExecRes = execl\("/bin/sh", "sh", "-c", szStartCmd.c_str\(\), \(char *\)0\);		
		
\}

sleep\(1\);


std::cout << "I'm a child process of : " << getppid\(\) << ", my pid is " << getpid\(\) << std::endl;


// Wait for the child process to end, but without blocking !
// If the child process exits immediately, we assume ftp couldn't be started -> return false
// else the child process is waited for later \(it becomes a "zombie" process when ftp is closed, 
// by calling waitpid, the zombie processes are cleared.
int nChildDied = waitpid\(pid, &nStatus, WNOHANG\);

if \(nChildDied == 0\)
\{ 
	// Child is not dead -> protocol analyser was started OK
 	std::cout << "ftp started, child not dead" << std::endl;

\}	
else if \(nChildDied > 0\)
\{
	// A child process died -> error starting protocol analyser 
  	if \(WIFEXITED\(nStatus\)\) 
	\{
	 	std::cout << "child process with pid = " << nChildDied << " died. Child WIFEXITED status = " 
 					<< nChildDied << WEXITSTATUS\(nStatus\);		  	
	\}
	if \(WIFSIGNALED\(nStatus\)\) 
	\{
	  	std::cout << "child process with pid = " << nChildDied << "died. Child WIFSIGNALED status = " 
					<< nChildDied << WTERMSIG\(nStatus\);		  				  	
	\}
	
 	std::cout << "error starting ftp";								

\}
else if \(nChildDied < 0\)
\{
	// An error occured
    if \(errno == ECHILD\) 
    \{
		std::cout << "no child process exists";								
    \}
    else if \(errno == EINTR\) 
    \{
		std::cout << "calling process interrupted by a signal";													
    \}
    else if \(errno == EINVAL\) 
    \{		
		std::cout << "bad argument passed to waitpid";													
    \}
\}	

char send[80], receive[80] = "initial value";

fprintf\(stdout, "Ftp Command ? > "\); fgets\(send, 80, stdin\);	

write\(fd[1],send,strlen\(send\)\);

read\(fd[0],receive,strlen\(receive\)\);

std::cout << "response from ftp : " << receive << std::endl; 

        fprintf\(stdout, "Ftp Command ? > "\); fgets\(send, 80, stdin\);	

write\(fd[1],send,strlen\(send\)\);

...

You can solve your problem with library functions - "popen", "pclose"

http://www.freebsd.org/cgi/man.cgi?query=popen&apropos=0&sektion=0&manpath=FreeBSD\+6.2-RELEASE&format=html

Best regards,
Iliyan Varshilov

hi
i wrote a reply that took me about an half our or more (my english is not good). when i clicked the "submit request" button another login was requested an all i had written was lost.
therefore a short version of my post:

  1. popen is not ansi c and you cannot both write and read from the pipe it creates, espesially not in solaris9 as the manual states.
  2. in m.bach,The design of the UNIX operating system,ISBN:0-13-201799-7
    one can find a description what one must do
  3. i found to examples in the net which i do not check:
    Mapping UNIX pipe descriptors to stdin and stdout in C
    Simple popen2 implementation [c] [unix] [ipc] [popen] [popen2] [pipes]
  4. in your program i am missing the second pipe. you correctly create one pipe and use the dup function to that the child's process stdin now is sent through the pipe. you must create another pipe for the stdout in the same manner
  5. after exec the filehandles are inherited so your ftp-progrma will read from one pipe and write to the other. but everything in the program beinning with the line
    sleep(1);
    will never be executed by the child (but by the parent)

now i make a copy of this text befor i press the submit-button
mfg guenter

If you get another login screen and you then login, your operation will be completed, did you do this or did you press "back" on your browser?

hi

i've got a login screen and i think opened another window from where i went back and then i logged in int the first windwow but i am not sure about that.
mfg guenter