0

I have a weird issue with a server written in C.

I have a function that is treating each client that connects to my server, so this function calls a fork() for each connection that the server receives.

The issue is that I have other forks inside that client-fork that are supposed to write an exec()'s output inside a file that I created .I dup2() that file's descriptor with stdout so that the output can be read from the file inside the parent right after the second child process (the one with exec() call) dies.

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
#include <fcntl.h>

#define PORT 2021

extern int errno;

void signal_handler( int sig )
{
  printf("[Server]Cought signal SIGCHLD\n");
  while( waitpid(-1, NULL, WNOHANG) > 0 )
    {
    }
}

int TreatClient(int clientSocket, int nr)
{
    int pid1,wait1;
    if ((pid1 = fork()) == -1)
    {
        printf("eroare pid");
        exit(1);
    }
    if(pid1 == 0) // treat client 
    {
        printf("START TREAT CLIENT!\n");
        nr++;
        int saved_stdout;
        char filename[3];
        snprintf(filename, 3,"%d",nr);
        int pid2,wait2;
        saved_stdout = dup(1);
        if ((pid2 = fork()) == -1)
        {
            printf("eroare pid");
            exit(1);
        }
        if(pid2 == 0)
        {
            printf("START DUP child!\n");
            int fd = open("fisier",O_CREAT|O_RDWR,00700);
            dup2(fd,1);
            printf("child 2 !\n");
            close(fd);
            return 1;
        }
        else
        {
            int fil;
            char chara;
            wait(&wait2);
            dup2(saved_stdout,1);
            close(saved_stdout);
            fil = open("fisier",O_RDONLY,00700);
            while(read(fil,&chara,1)!=-1 && chara != '\n')              printf("%c",chara);
            printf("\nEND PARENT dup !\n");
        }
        printf("TREAT CLIENT END!\n");
        return 1;
    }
    else
    {
        wait(&wait1);
        printf("END MAIN!\n");
    }
    return 1;
    }               /* while */
  return 0;
    return 1;
}

int main()
{
  struct sockaddr_in server;    
  struct sockaddr_in from;  
  char msg[100];        
  char rply[100]=" ";        
  int sd;           
  int on, status, nr = 0;

  if( signal(SIGCHLD, signal_handler) < 0 )
    {
      printf("[Server]Signal Error : %d !\n", errno);
      exit(-5);
    }

  if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
    {
      perror ("[server]Socket Error !\n");
      return errno;
    }

  on = 1;
  status = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (const char *) &on, sizeof(on));

  if ( status == -1 )
    {
      printf("[server]Setsockopt() Error : %d !\n", errno);
      exit(-1);
    }

  bzero (&server, sizeof (server));
  bzero (&from, sizeof (from));

    server.sin_family = AF_INET;    
    server.sin_addr.s_addr = htonl (INADDR_ANY);
    server.sin_port = htons (PORT);

  if (bind (sd, (struct sockaddr *) &server, sizeof (struct sockaddr)) == -1)
    {
      printf("[server]Bind Error : %d!\n", errno);
      exit(-2);
    }

  if (listen (sd, 5) == -1)
    {
      printf ("[server]Listen Error : %d!\n", errno);
      exit(-3);
    }
    ///////////////////////////////////////////////////////////////////////////////////////////
  /* client-iteration*/
  while (1)
    {
      int client;
      int length = sizeof (from);

      printf ("[server]Waiting at port %d...\n",PORT);
      fflush (stdout);

      client = accept (sd, (struct sockaddr *) &from, &length);
      nr++;

      if (client < 0)
    {
      printf ("[server]Accept Error : %d!\n", errno);
      continue;
    }

      printf("[server]Client number %d connected\n", nr);
      TreatClient(client, nr);
      return 1;
}

The desired output is simmilar to this :

[server]Waiting at port 2021...
[server]Client number 1 connected
START TREAT CLIENT!
START DUP child!
[Server]Cought signal SIGCHLD
child 2 !
END PARENT dup !
TREAT CLIENT END!
[Server]Cought signal SIGCHLD
END MAIN!

The code does not produce the desired output. It becomes stuck after DUP2 and the rest of the output is redirected towards the file. But if i replace the TreatClient(client, nr); line with the function block the code works as it should. Why is this happening ? Another weird issue is that the message [server]Waiting at port 2021... is also printed in the file and not on the screen .

Edeph
  • 732
  • 2
  • 12
  • 29
  • 1
    `char filename[3];` 3 might be a bit too small – wildplasser Jan 02 '14 at 11:28
  • @wildplasser no that part works fine each client has a number by the order of arrival (1,2...) and each client will have his own file for those commands. – Edeph Jan 02 '14 at 11:29
  • A mutex is used to sync threads. I don't think they work with subprocesses. – Krumelur Jan 02 '14 at 11:30
  • @Krumelur http://dev.yorhel.nl/doc/sqlaccess source. And i don't think that is an issue to my dup2 issue – Edeph Jan 02 '14 at 11:32
  • After 100 messages you will overflow the string, and it is very dangerous not to use the same length in snprintf. – Krumelur Jan 02 '14 at 11:32
  • Well, in that case `snprintf(filename, 10, ...)` should have been `snprintf(filename, 3,...` – wildplasser Jan 02 '14 at 11:35
  • @Krumelur i won't get anywhere near to 100 messages this is just a very small project (college) and it's not aimed for enterprise or anything like that. – Edeph Jan 02 '14 at 11:35
  • 3
    Yes, but you are not creating threads, you are forking. From Sqlite FAQ: "Under Unix, you should not carry an open SQLite database across a fork() system call into the child process. Problems will result if you do." – Krumelur Jan 02 '14 at 11:35
  • @Krumelur thanks noted but it has no effecto n my dup2 issue – Edeph Jan 02 '14 at 11:36
  • Why you snprintf() with size 10 to char [3]? That might not be your current problem, but do matter. – moeCake Jan 02 '14 at 11:37
  • @moeCake corrected the snprintf – Edeph Jan 02 '14 at 11:38
  • Well, you wrote it was crashing. – Krumelur Jan 02 '14 at 11:41
  • @Krumelur check the update code, it acts the same after i eliminate any trace of sqlite stuff and i corrected the snprintf – Edeph Jan 02 '14 at 11:48
  • 2
    Why is the second child returning instead of exiting? return will have very different semantics when you move the code block into a function. – William Pursell Jan 02 '14 at 16:18
  • @WilliamPursell this was the issue thank you so much ! – Edeph Jan 03 '14 at 09:09

1 Answers1

1

When the second child is finished, it should exit instead of returning.

William Pursell
  • 204,365
  • 48
  • 270
  • 300
  • Could you explain a little why return messes up the child process behavior ? – Edeph Jan 03 '14 at 13:15
  • It's hard to see exactly what happens because the code in the question doesn't compile. It has mismatched braces. –  Jan 03 '14 at 16:20
  • 1
    @Edeph When the 2nd child returns, it continues executing code in the caller. The process that is `wait`ing for it to exit probably hangs since it does not exit, but as @Wumpus points out it's hard to say exactly what happens without more code. – William Pursell Jan 06 '14 at 13:53