2

I have either completely forgotten everything I thought to know about C, or something is going terribly wrong. I want to redirect the output of an subprocess (stdout and stderr) to a file. I do this this way:

if ((pid = fork()) == 0)
{
   get_host_date(today) ;
   int fd = open(log_filename, O_RDWR | O_CREAT | O_APPEND,
                 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH) ;
   dup2(fd, STDOUT_FILENO) ;     // make stdout go to file
   dup2(fd, STDERR_FILENO) ;     // make stderr go to file
   fprintf(stderr, "This is a message") ;

   if ((status = execv(command, argv)) == -1)
   {
      printf("\n\nError: execve failed for %s", command) ;
      perror("\nexecve failure: ") ;         
      exit(EXIT_FAILURE) ;
   }
   close(STDOUT_FILENO) ;
   close(STDERR_FILENO) ;
}

The log file is created as specified, but the output does not go to the file. I made some test output (fprintf(stderr, "This is a message") ;), but this does not appear anywhere. Wenn I checked the variable fd, I saw that it got the value of 1 (immediately after open). But should 1 not be the predefined output file descriptor?

Can anybody help me? I tried everything, but came to no result.

Thank you very much in beforehand Best wishes Jörg

P.S.: I'm working with RHEL and GNU-C.

Ok, here a comprimed code which does not work with me:

int main(int   argc,
         char* argv[])
{
   PROC_REC    proc ;

   strcpy(proc.command, "/home/islk/entw/v0816/exe/islk_server") ;
   strcpy(proc.args[0], "ISLK_DB1_SERV01") ;
   strcpy(proc.args[1], "") ;
   strcpy(proc.env[0], "ISLKSERVER_NR=5") ;
   strcpy(proc.env[1], "") ;

   ISLK_start_single_process(&proc) ;

   exit(EXIT_SUCCESS) ;
}

static long ISLK_start_single_process(PROC_REC *prec_ptr)
{
   long     i=0 ;
   int      status ;
   char     *argv[16] ;
   char     command[256], log_filename[128], today[DB_DATE_DOM] ;
   pid_t    pid ;

   /* Set arguments */
   for (i=0; i<16; i++)
   {
      if (strcmp(prec_ptr->args[i], "") != 0)
         argv[i] = prec_ptr->args[i] ;
      else
         argv[i] = NULL ;
   }

   /*******************/
   /* Set environment */
   /*******************/
   for (i=0; i<16; i++)
   {
      if (strcmp(prec_ptr->env[i], "") != 0)
         putenv(prec_ptr->env[i]) ;
   }

   /*****************/
   /* Start process */
   /*****************/
   if ((pid = fork()) == 0)
   {
      get_host_date(today) ;
      bs_create_filename(log_filename, "islk$log", "", "%s_%8.8s.log",
                         prec_ptr->args[0], today) ;
      int fd = open(log_filename, O_RDWR | O_CREAT | O_APPEND,
                    S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH) ;
      int fdo = dup2(fd, STDOUT_FILENO) ;     // make stdout go to file
      int fde = dup2(fd, STDERR_FILENO) ;     // make stderr go to file
      close(fd) ;    
      fprintf(stdout, "This is a message") ;
      if ((status = execv(command, argv)) == -1)
      {
         printf("\n\nError: execv failed for %s", command) ;
         perror("\nexecv failure: ") ;         
         exit(EXIT_FAILURE) ;
      }
      close(STDOUT_FILENO) ;
      close(STDERR_FILENO) ;
   }
   else if (pid < 0)
   {
      printf("\n\nError: fork failed for %s", prec_ptr->args[0]) ;
      perror("\nfork failure: ") ;
      return ERROR ;
   }
   else
   {
      printf("\nProcess %d started for %s", pid, prec_ptr->args[0]) ;
      prec_ptr->pid = pid ;
   }

   return NO_ERROR ;
}

Tried the following:

dup2(fd, STDERR_FILENO) ;
dup2(fd, STDOUT_FILENO) ;

-> messages to stderr are written in file, to stdout not

dup2(fd, STDOUT_FILENO) ;

-> messages to stderr are written in terminal, to stdout nowhere

dup2(fd, STDOUT_FILENO) ;
dup2(STDERR_FILENO, STDOUT_FILENO) ;

-> messages to stderr are written in file, to stdout on terminal

dup2(fd, STDERR_FILENO) ;
dup2(STDOUT_FILENO, STDERR_FILENO) ;

-> messages to stderr are written in file, to stdout on terminal

-> Something seems to be a problem with stdout !

  • I cannot reproduce your problem. Please provide a [mcve]. If I add the “obvious” bits to make your code compile cleanly, it behaves as expected. – 5gon12eder Mar 03 '16 at 15:47
  • Maybe use `fileno` to determine the integer descriptor. Also you should close fd after `dup2`. And I am unsure if you can use one file for both streams. – Konstantin W Mar 03 '16 at 15:49
  • 1
    I think you're right with `fd == 1` being part of the problem. It seems that `stdout` has been closed before by your process or one of its parents and whatever happens there exactly could also prevent your redirection from working (e.g. `dup2(1, 1);` doesn't make sense, but I don't know if it would do harm) – Ingo Leonhardt Mar 03 '16 at 16:29
  • off topic: the `close()` commands will never be executed. If `exec..()` succeeds it will never return and if it fails you're `exit()`ing before you would call `close()` – Ingo Leonhardt Mar 03 '16 at 16:32
  • **always** check for errors. Please add the appropriate checks to *every* function, and report your findings (including errno) – Daniel Jour Mar 03 '16 at 16:42
  • Konstantin: Tried it with only one redirection as well, does not work neither Ingo: I thought so as well because the process redirects its own output before (process is converted to a daemon with output to a file, which then starts subprocesses which should all have an separate logfile). But I simplified the program as shown above, and it does not work either. Daniel: open returned 1, dup2 returned 1 and 2 – Jörg Mohren Mar 03 '16 at 16:44
  • @DanielJour: In the simplified program: fd = open(...) -> returned fd=3 fdo = dup2(fd, 1) -> returned 1 fde = dup2(fd, 2) -> returned 2 – Jörg Mohren Mar 03 '16 at 16:52
  • Mhm ... is stdout perhaps marked as `FD_CLOEXEC` for what ever reason. Though that wouldn't explain why the `fprintf` before the `execv` isn't working. Can you try the approach listed in this answer (not the accepted one): http://stackoverflow.com/a/14543455/1116364 – Daniel Jour Mar 03 '16 at 18:40
  • Also (don't know whether you saw my answer that i deleted in the mean time) ... even if it seems dumb .. could you exchange the arguments to `dup2` ? – Daniel Jour Mar 03 '16 at 18:43

3 Answers3

0

I think you have nearly answered the question by yourself by detecting that fd == 1 "immediately after open()". According to the dup2() man page:

The dup2() system call performs the same task as dup(), but instead of using the lowest-numbered unused file descriptor, it uses the descriptor number specified in newfd. If the descriptor newfd was previously open, it is silently closed before being reused.

So if fd == 1 and you call dup2( fd, STDOUT_FILENO ); then STDOUT_FILENO (which is 1 too) is closed and so is the descriptor to your redirection file. If you can't find out why stdout is closed in your program's environment maybe you could just do a test before calling dup2():

if( fd != STDOUT_FILENO )
    dup2( fp, STDOUT_FILENO );

At least that should work as a trick to check if I'm right.

Ingo Leonhardt
  • 9,435
  • 2
  • 24
  • 33
  • I found out why fd == 1 (closed stdout&stderr before). But I changed this (now fd == 3 after open). I am now so far that stderr is correctly written. But the messages to stdout just don't appear in the output file, even if I only redirect stdout (see above) – Jörg Mohren Mar 03 '16 at 17:07
  • probably a dumb question, but are you sure that the`exec()`d command actually prints sth. to stdout? At least you should add a `fflush(stdout)` after `printf()` otherwise it will maybe just be buffered internally – Ingo Leonhardt Mar 03 '16 at 17:18
  • :I made an fprintf(stdout, ...) and fprintf(stderr, ...) even before the exec(...). stderr came into the file, stdout not. Tomorrow I will try a fflush, perhaps it will help – Jörg Mohren Mar 03 '16 at 18:31
0

I now sinplified my program even more:

int main(int   argc,
         char* argv[])
{
   pid_t pid ;

   int fd = open("main.log", O_RDWR | O_CREAT | O_APPEND,
                 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH) ;
   dup2(fd, STDERR_FILENO) ;     // make stdout go to file
   dup2(fd, STDOUT_FILENO) ;     // make stderr go to file      
   close(fd) ;
   fprintf(stdout, "Main: This is an output message") ;
   fprintf(stderr, "Main: This is an error message") ;
   fflush(stdout) ;
   fflush(stderr) ;

   if ((pid = fork()) == 0)
   {
      fd = open("sub.log", O_RDWR | O_CREAT | O_APPEND,
                    S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH) ;
      dup2(fd, STDERR_FILENO) ;     // make stdout go to file
      dup2(fd, STDOUT_FILENO) ;     // make stderr go to file      
      close(fd) ;    
      fprintf(stdout, "Sub: This is an output message") ;
      fprintf(stderr, "Sub: This is an error message") ;
      fflush(stdout) ;
      fflush(stderr) ;
      exit(0) ;
   }

   exit(EXIT_SUCCESS) ;
}

which gives me

::::::::::::::
main.log
::::::::::::::
Main: This is an error messageMain: This is an output message
::::::::::::::
sub.log
::::::::::::::
Sub: This is an error messageSub: This is an output message

So, there must be something wrong in the called process. It makes output to stdout, but there must be something that hinders him from doing so when called as a subprocess (called interactively, there is lots of output on stdout). If anybody has an idea what might be the reason, I would be thankful for any further help. Thanks a lot for your help up to this point.

Jörg

0

Eureka, I found it.

We call a library function from an external vendor. Before this call, everything works fine, after it, there is not output to stdout anymore.

I now contacted the vendor to ask what they do there. Meanwhile, this helps:

int saved_stdout = dup(STDOUT_FILENO) ;
// vendor library call
dup2(saved_stdout, STDOUT_FILENO) ;

Thanks to you all for your help

Best wishes, and a great weekend

Jörg