0

I am trying to create a simple client/server program that allows the client to connect to the server using a TCP socket and then allows the user to issue system calls form the client side to the server side and return the reply to the user. For example:

Client issues: ls

Server will find ls in /usr/bin or w/e and then execute it using execve()

I will also have something liks lls, or lmkdir, ect..which will issue the system calls on the client side.

The problem is my execve() is not appearing to run correctly because 'ls' or any other command is not actually being called. I have done this same kind of program before with only a local side (no server or anything) and execve() worked fine. Here is some code:

       pid = fork();

       if(pid){ // Child
        printf("child wait");
          pid = wait(&status);
          printf("Child dead\n");
       }else{ // Parent

          if(execPath){            

          execve(execPath, arglist, env);
          printf("Command Complete\n");

        }

       }

For some reason the printfs in the child section of the PID statement are not executing at all. I do not think the system is actually ever forking a process. Is there something special I would have to do to make this work since it is a client/server type of program or should it work exactly the same?

Thanks

Matt Hintzke
  • 7,744
  • 16
  • 55
  • 113

3 Answers3

2

exactly, execve does not fork. It replaces current image with the one specified as its argument and starts from its start (i.e. main()). It never returns to your origial program.

You probably want to use system() in your use case.

mity
  • 2,299
  • 17
  • 20
1

Since the child process has not spawned any children of its own, the wait() call is unlikely to return without some other external event (like a signal interrupting the call). You should have the parent wait on the child process instead.

Note that fork() may fail, and you should account for that. Also note that if execve succeeds, it won't return. So, the print statement after it should indicate failure if it is to print anything at all.

Using system() probably would not save you the fork, since you are likely to want the output of the command to be directed to the socket associated with the connected client. But, your code is missing the steps that would allow the output to flow to the client.

switch ((pid = fork())) {
case -1:  /* todo: handle error */
          break;
case 0:   /* child */
          dup2(socket, 0); /* todo: check return value */
          dup2(socket, 1); /* todo: check return value */
          dup2(socket, 2); /* todo: check return value */
          close(socket);   /* todo: check return value */
          execve(...);
          /* todo: handle failure */
          exit(EXIT_FAILURE);
default:  /* parent */
          if (pid != waitpid(pid, 0, 0)) {
              /* todo: handle error */
          }
}
jxh
  • 69,070
  • 8
  • 110
  • 193
1

There are several problems in the code:

  • fork() returns pid for the parent and zero for the child. So parent runs the true branch of the if. And child runs the else branch. Swap those comments.
  • The stdout is line buffered. Add new line (\n) to printf which is before the wait. Or else you don't see the printout before waiting is done and 2nd printf is under call.
  • Be sure that child will exit also in error cases, or else the child will run the code of parent, and parent is still waiting exit of the child.
  • execve does not return if it success. It will return, if it fails.

So, fixed code could be something like that:

   pid = fork();

   if(pid){  // Parent
      printf("child wait\n");
      pid = waitpid(pid, &status, 0);
      printf("Child dead\n");
   }else{ // Child
      if(execPath){            
          execve(execPath, arglist, env);
          printf("execve failed!\n");
      }
      _exit(1);
   }

Or you could use system(3).

SKi
  • 8,007
  • 2
  • 26
  • 57