LinuxQuestions.org
Share your knowledge at the LQ Wiki.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 11-24-2010, 02:15 PM   #1
AndrewBS42
LQ Newbie
 
Registered: May 2006
Posts: 7

Rep: Reputation: 1
Angry Re-directing stdin, stdout, stderr for program run as fork()/execl()


I'm trying to write a program that will fork a series of FTP sessions. For each session, there should be separate input and output files associated with stdin and stdout/stderr.
I keep reading how I should be able to do that with dup2() in the child process before the execl(), but it's not working for me. Could someone please explain what I've done wrong?

The program also has a 30-second sniper alarm for testing and killing of FTPs that go dormant for too long.

Thanks in advance for anything you can offer.
--
AndrewBS42@hotmail.com

The code: (ftpmon.c)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>

void imgPull_ftp_timeout_action( int signo);

pid_t child_pid = 0;

void imgPull_ftp_timeout_action( int signo)
{
int result = 0;

/* timeout! Kill the child */
if ((result = kill(child_pid, SIGTERM)) != 0)
{
perror("kill()");
}

return;
}

int main( int argc,
char *argv[])
{
int status = 0;

int script_fd = 0;
int log_fd = 0;
int dummy = 0;
int child_status = 0;
mode_t log_mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);

pid_t term_pid = 0;

char *ftp_executable = "/usr/bin/ftp";
char *script_name = "/home/abs/ftp/script.ftp";
char *log_name = "/home/abs/ftp/script.ftp.log";

struct sigaction img_sigaction_alrm;

/* Set up signal handlers for alarm */
memset(&(img_sigaction_alrm), '\0', sizeof(img_sigaction_alrm));
img_sigaction_alrm.sa_handler = imgPull_ftp_timeout_action;

sigaction(SIGALRM, &(img_sigaction_alrm), NULL);

/* fork() */
alarm(30);
child_pid = fork();
if (child_pid == -1)
{
/* error */
perror("fork()");
return(1);
}
else if (child_pid == 0)
{
/* I am child. */

/* Set up to re-direct my input and output to files */
/* Open (for read) what will be the stdin for the FTP */
if ((script_fd = open(script_name, O_RDONLY)) == -1)
{
perror("open() for script");
exit(1);
}

/* Open (for write) what will be the stdout/stderr for the FTP */
if ((log_fd = creat(log_name, log_mode)) == -1)
{
perror("open() for log");
exit(1);
}

/* Call dup2() to point the child's stdin to the script */
if ((dummy = dup2(STDIN_FILENO, script_fd)) == -1)
{
perror("dup2() for script (stdin)");
exit(1);
}

/* Call dup2() to point the child's stdout to the log */
if ((dummy = dup2(STDOUT_FILENO, log_fd)) == -1)
{
perror("dup2() for log (stdout)");
exit(1);
}

/* Call dup2() to point the child's stderr to the log */
if ((dummy = dup2(STDERR_FILENO, log_fd)) == -1)
{
perror("dup2() for log (stderr)");
exit(1);
}

close(script_fd);
close(log_fd);

/* do FTP */
if ((dummy = execl(ftp_executable,
ftp_executable, "-i", "-n", "-v", "gila-crstest.gilacorp.com", (char *) NULL)) == -1)
{
perror("execl()");
exit(1);
}
}
else
{
/* I am parent. */

/* Wait for child to end */
if ((term_pid = waitpid(child_pid, &child_status, 0)) == -1)
{
perror("waitpid()");
exit(1);
}

/* disable alarm */
alarm(0);

if (WIFEXITED(child_status))
{
printf("child exited normally, status = %d\n", WEXITSTATUS(child_status));
}

if (WIFSIGNALED(child_status))
{
printf("child terminated by uncaught signal %d\n", WTERMSIG(child_status));
}
}

return(0);
}


The FTP script: (script.ftp) (I want this to be stdin)
user abs NoneOfYourBusiness
ascii
hash
cd ftp
get interesting.txt
bye

The output:
$ ftpmon
Connected to gila-crstest.gilacorp.com (172.16.20.8).
220 (vsFTPd 2.0.1)
ftp> waitpid(): Interrupted system call

Why am I getting the ftp> prompt? If the dup2() works, shouldn't it be taking input from my script and not my terminal? In stead, it does nothing, and winds up getting killed after 30 seconds.

The log file is created, but it's empty after the run.
 
Old 11-25-2010, 05:08 PM   #2
theNbomr
LQ 5k Club
 
Registered: Aug 2005
Distribution: OpenSuse, Fedora, Redhat, Debian
Posts: 5,399
Blog Entries: 2

Rep: Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908
I will throw out a couple of possibilities.
The FTP application will issue prompts, and then expect a reply that corresponds to the prompt. If you feed it a complete file, the first read of its standard input may be swallowing the whole file.
The other is that FTP may not be using standard IO to request and get user input. Since it probably needs to handle password input, it may be bypassing standard IO, and opening a connection to the terminal directly. To test this theory, you could contrive a different script that applies to something else which you know uses standard IO; perhaps a simple test program that you also contrive that does writes and reads using printf() and scanf().

Upon cursory inspection of your code, I see no obvious problem. I would have looked more closely, if you had posted your code in [CODE ] tags to preserve the formatting and make it readable.

--- rod.

Last edited by theNbomr; 11-25-2010 at 05:12 PM.
 
Old 11-25-2010, 07:35 PM   #3
wje_lq
Member
 
Registered: Sep 2007
Location: Mariposa
Distribution: FreeBSD,Debian wheezy
Posts: 811

Rep: Reputation: 179Reputation: 179
In the name of the Flying Spaghetti Monster

I do believe you have the file descriptors reversed on your dup2() calls.
 
1 members found this post helpful.
Old 11-26-2010, 08:35 AM   #4
AndrewBS42
LQ Newbie
 
Registered: May 2006
Posts: 7

Original Poster
Rep: Reputation: 1
Thumbs up

theNbomr:
I did try running the FTP script from the command line with the < and > shell re-direction operators, and it worked, so it looked like a safe bet that FTP was using stdin and stdout. I was previously unaware of the [ code ] tag. Thanks for the information.

The prize goes to lje_wq.

Quote:
I do believe you have the file descriptors reversed on your dup2() calls.
The significance of the pirate ship in last night's dreams is becoming evident. I'll never doubt the power of the Flying Spaghetti Monster again.

wje_lq was correct. I had my arguments to dup2() reversed. What I wanted was to modify the STDIN_FILENO descriptor to look like the one that described my open script, not the other way around.

The modified code works for me now, and is included below. Thanks WJE.

Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>

void    imgPull_ftp_timeout_action(     int                 signo);

pid_t   child_pid   = 0;

void    imgPull_ftp_timeout_action( int         signo)
{
    int result  = 0;

    /* timeout!  Kill the child */
    if ((result = kill(child_pid, SIGTERM)) != 0)
    {
        perror("kill()");
    }

    return;
}


int main(   int     argc,
            char    *argv[])
{
    int     status      = 0;

    int     script_fd       = 0;
    int     log_fd          = 0;
    int     dummy           = 0;
    int     child_status    = 0;
    mode_t  log_mode        = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);

    pid_t   term_pid        = 0;

    char    *ftp_executable = "/usr/bin/ftp";
    char    *script_name    = "/home/abs/ftp/script.ftp";
    char    *log_name       = "/home/abs/ftp/script.ftp.log";

    struct sigaction    img_sigaction_alrm;

    /* Set up signal handlers for alarm */
    memset(&(img_sigaction_alrm),   '\0', sizeof(img_sigaction_alrm));
    img_sigaction_alrm.sa_handler   = imgPull_ftp_timeout_action;

    sigaction(SIGALRM, &(img_sigaction_alrm), NULL);

    /* fork() */
    alarm(30);
    child_pid = fork();
    if (child_pid == -1)
    {
        /* error */
        perror("fork()");
        return(1);
    }
    else if (child_pid == 0)
    {
        /* I am child. */

        /* Set up to re-direct my input and output to files */
        /* Open (for read) what will be the stdin for the FTP */
        if ((script_fd = open(script_name, O_RDONLY)) == -1)
        {
            perror("open() for script");
            exit(1);
        }

        /* Open (for write) what will be the stdout/stderr for the FTP */
        if ((log_fd = creat(log_name, log_mode)) == -1)
        {
            perror("open() for log");
            exit(1);
        }

        /* Call dup2() to point the child's stdin to the script */
        if ((dummy = dup2(script_fd, STDIN_FILENO)) == -1)
        {
            perror("dup2() for script (stdin)");
            exit(1);
        }

        /* Call dup2() to point the child's stdout to the log */
        if ((dummy = dup2(log_fd, STDOUT_FILENO)) == -1)
        {
            perror("dup2() for log (stdout)");
            exit(1);
        }

        /* Call dup2() to point the child's stderr to the log */
        if ((dummy = dup2(log_fd, STDERR_FILENO)) == -1)
        {
            perror("dup2() for log (stderr)");
            exit(1);
        }

        close(script_fd);
        close(log_fd);

        /* do FTP */
        if ((dummy = execl(ftp_executable,
                ftp_executable, "-i", "-n", "-v", "gila-crstest.gilacorp.com", (char *) NULL)) == -1)
        {
            perror("execl()");
            exit(1);
        }
    }
    else
    {
        /* I am parent. */

        /* Wait for child to end */
        if ((term_pid = waitpid(child_pid, &child_status, 0)) == -1)
        {
            perror("waitpid()");
            exit(1);
        }

        /* disable alarm */
        alarm(0);

        if (WIFEXITED(child_status))
        {
            printf("child exited normally, status = %d\n", WEXITSTATUS(child_status));
        }

        if (WIFSIGNALED(child_status))
        {
            printf("child terminated by uncaught signal %d\n", WTERMSIG(child_status));
        }
    }

    return(0);
}
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
redirecting stdin, stdout and stderr, and finding files name and other stats wroom Programming 4 08-14-2010 06:48 AM
[SOLVED] How to get stderr and stdout of a program into separate env variables? 10110111 Linux - Software 2 05-20-2010 06:17 AM
Shell script stdout, stderr and stdin solo9300 Linux - General 6 12-29-2009 12:33 AM
how to change stdin, stdout & stderr in u-boot to ethernet from serial jynthms Linux - Newbie 1 04-01-2009 09:34 AM
stdout,stderr makes program crash surfchen Linux - Newbie 3 03-11-2008 05:27 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 08:05 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration