Search code examples
c++crpc

Restarting RPC service


I have a client process that forks a child process to listen for incoming RPCs via the svc_run() method. What I need to do is kill of that child process from the parent and then re-fork the child process providing it a new CLIENT* to a new RPC server.

Here is the bits of my code that are relevant:

// Client Main
CLIENT* connectionToServer;
int pipe[2];
int childPID;
int parentPID;

static void usr2Signal()
{
  ServerData sd;
  clnt_destroy(connectionToServer);
  (void) read(pipe[0], &sd, sizeof(sd));


  // Kill child process.
  kill(childPID, SIGTERM);
  close(pipe[0]);


  // RPC connection to the new server
    CLIENT *newServerConn =
        clnt_create(
          sd.ip,
          sd.programNum,
          1,
          "tcp");

    if (!newServerConn)
    {
        // Connection error.
        exit(1);
    }

    connectionToServer = newServerConn;


  // Respawn child process.
  if (pipe(pipe) == -1)
  {
      // Pipe error.
      exit(2);
  }

  childPID = fork();
  if (childPID == -1)
  {
    // Fork error.
    exit(3);
  }
  if (childPID == 0)
  {
    // child closes read pipe and listens for RPCs.
      close(pipe[0]);
      parentPID = getppid();
      svc_run();
  }
  else
  {
    // parent closes write pipe and returns to event loop.
    close(pipe[1]);
  }
}

int main(int argc, char *argv[])
{
    /* Some initialization code */

    transp = svctcp_create(RPC_ANYSOCK, 0, 0);
    if (transp == NULL) {
        // TCP connection error.
        exit(1);
    }

    if (!svc_register(transp, /*other RPC program args*/, IPPROTO_TCP))
    {
        // RPC register error
        exit(1);
    }



  connectionToServer = clnt_create(
        192.168.x.xxx, // Server IP.
        0x20000123,     // Server RPC Program Number
        1,              // RPC Version
        "tcp");

  if (!connectionToServer)
  {
    // Connection error
    exit(1);
  }

  // Spawn child process first time.
  if (pipe(pipe) == -1) 
  {
    // Pipe error
    exit(1);
  }

  childPID = fork();
  if (childPID == -1)
  {
    // Fork error.
    exit(1);
  }

  if (childPID == 0)
  {
    // Close child's read pipe.
    close(pipe[0]);
    parentPID = getppid();

    // Listen for incoming RPCs.
    svc_run ();
    exit (1);
  }


  /* Signal/Communication Code */

  // Close parent write pipe.
  close(pipe[1]);

  // Parent runs in event loop infinitely until a signal is sent.
  eventLoop();
  cleanup();
}

In my server code I have service call that initiates the new connection. This call is invoked by some other operation on the server.

// Server Services
void newserverconnection_1_svc(int *unused, struct svc_req *s)
{
    // This service is defined in the server code

    ServerData sd;
    /* Fill sd with data:
         Target IP: 192.168.a.aaa
         RPC Program Number: 0x20000321
         ... other data
    */

    connecttonewserver_1(&sd, connectionToServer); // A client service.
}

Back in my client I have the following service:

// Client Service
void connecttonewserver_1_svc(ServerData *sd, struct svc_req *s)
{
    // Send the new server connection data to the parent client processs
    // via the pipe and signal the parent.
    write(pipe[1], sd, sizeof(sd));
    kill(parentPID, SIGUSR2);
}

My problem is, everything runs fine until I initiate the new connection. I do not get into any of my error sections, but about 5 seconds after setting up the new connection, my client becomes unresponsive. It does not crash and the child process seems to still be alive also, but my client will no longer receive RPCs or show any print statements when my events defined in the event loop for the parent are triggered by mouse clicks. I am probably doing something slightly wrong to spawn this new RPC loop for the child process, but I can't see what. Any ideas?


Solution

  • So this solution achieves the result I was looking for, but is definitely far from perfect.

    static void usr2Signal()
    {
      ServerData sd;
      // clnt_destroy(connectionToServer); // Removed this as it closes the RPC connection.
      (void) read(pipe[0], &sd, sizeof(sd));
    
    
      // Removed these. Killing the child process also seems to close the
      // connection. Just let the child run.
      // kill(childPID, SIGTERM);
      // close(pipe[0]);
    
    
      // RPC connection to the new server
        CLIENT *newServerConn =
            clnt_create(
              sd.ip,
              sd.programNum,
              1,
              "tcp");
    
        if (!newServerConn)
        {
            // Connection error.
            exit(1);
        }
    
        // This is the only necessary line. Note that the old 
        // connectionToServer pointer was not deregistered/deallocated,
        // so this causes a memory leak, but is a quick fix to my issue.
        connectionToServer = newServerConn;
    
    
        // Removed the rest of the code that spawns a new child process
        // as it is not needed anymore.
    
    }