-1

I am trying to execute a program with the execvp function within an overseer and client distributed system. The client sends a program to be executed with the arguments:

char buf[500];
int bytes_recieved = 0;
char array[1][26];
bytes_recieved = recv(clientfd, buf, 5000, 0);
buf[bytes_recieved] = '\0';

char buf1[50];
int bytes_recieved1 = 0;
char *array1[4];

for (int i = 0; i < 3; i++){
   bytes_recieved1 = recv(clientfd, buf1, 50, 0);
   array1[i] = buf1;
   printf("%s = buffer\n", buf1);
} 
buf1[bytes_recieved] = '\0';

if(bytes_recieved != -1){
   printTime();
   fprintf(stdout,"atempting to execute program: %s\n", buf);
   if(execvp(buf, array1) == -1) {
      return 1;
   }
}

I'm stuck on trying to figure out what happens when I print out the array of arguments in the program the last argument is the same for all of them? for example I run this in the client program to be executed:

./client 12345 home/user/test_program 1 2 3

the result from a simple printf is:

3
3
3

When I manually assign each argument in the array in the overseer:

array1[0] = "1";
array1[1] = "2";
array1[2] = "3";

and send it to the executed program it prints correctly.

I have also tested that the received buffer from the file descriptor is correctly assigning the variables in the array:

 printf("%s = buffer\n", array1[i]);

within the assignment for loop, which returns:

    1 = buffer
    2 = buffer
    3 = buffer 

What am I doing wrong? Let me know if you need any more information.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
James
  • 3
  • 1
  • 1
    Ok first things first, you're using a TCP socket, this approach is **totally wrong**. How many bytes did you receive? Print out the `bytes_recieved`. It can be *less* than you request. That's how stream sockets work. **Then**, after that, you need to understand that you have only **one** buffer, that you assign to each element. Perhaps you need to copy the current string with for example `strdup` to have them *distinct*. `char array[1][26];` - `array[i] = something` does not even **compile** so that's not possibly the code that you're running. – Antti Haapala -- Слава Україні Oct 19 '20 at 12:57
  • Hi @AnttiHaapala, Thanks for your message. I am sending multiple times from the client, iterating through an array and sending the value that's why I'm using the same buffer within the for loop. Is that not the best solution is there to send the whole array in one buffer? apologies my naming conventions aren't that great, the array in the forloop is array1 not array. the first array value is the location of the program followed by the arguments attached – James Oct 19 '20 at 13:16
  • so it seems... so `array1[i] = strdup(buf);` is the solution to your immediate problem... but the code is still badly broken. – Antti Haapala -- Слава Україні Oct 19 '20 at 13:18
  • The mismatch between `char buf[500];` and `bytes_recieved = recv(clientfd, buf, 5000, 0);` is stark — some 4,500 bytes worth of of stack overflow available for use. Also, the mnemonic is "i before e except after c", so it is "receiving" etc. However, your misspelling is consistent and consistency is more important than spelling correctness with variable names. – Jonathan Leffler Oct 19 '20 at 13:29
  • Print the arguments to the `execvp()` call. You're not sending what you think you're sending. Your input loop repeatedly reads over `buf1`, so only the last argument survives. Don't forget that the second argument to `execvp()` includes the program name in element zero of the array. Normally, you'd use `execvp(argv[0], argv)` to ensure consistency. You don't need to test the result of `execvp()`; if it returns, it failed — if it succeeds, it does not return. It is good that you have some error handling code after it, though. – Jonathan Leffler Oct 19 '20 at 13:38
  • Hi Jonathan, you are right the last element is the same elements for all of the array. what do you that is the best solution to fix this? @JonathanLeffler – James Oct 19 '20 at 13:55
  • Strdup(buf) resolved the last element surviving issue. – James Oct 19 '20 at 13:57
  • Don't forget that you need to put a NULL pointer as the last element of `array1`. – Nate Eldredge Oct 19 '20 at 14:15
  • 1
    @JonathanLeffler thank you so much for your help, extremely appreciate you pointing me in the right direction. This is now working and I have implemented your suggestions. – James Oct 19 '20 at 21:55

1 Answers1

0

This is some skeletal code, based on your code fragment. I can't test it — you did not provide an MCVE (Minimal, Complete, Verifiable Example — or MRE or whatever name SO now uses) or an SSCCE (Short, Self-Contained, Correct Example).

char buf[500];
int bytes_received = recv(clientfd, buf, sizeof(buf)-1, 0);
if (bytes_received < 0)
    return 1;
buf[bytes_received] = '\0';

char *array1[5] = { buf };

for (int i = 0; i < 3; i++)
{
    char buf1[50];
    int bytes_received1 = recv(clientfd, buf1, sizeof(buf1)-1, 0);
    if (bytes_received1 < 0)
        return 1;
    buf1[bytes_received1] = '\0';
    array1[i + 1] = strdup(buf1);
    printf("argument = [%s]\n", buf1);
}

printTime();
printf("atempting to execute program: %s\n", buf);
for (int i = 0; array1[i] != NULL; i++)
    printf("argv[%d] = [%s]\n", i, array1[i]);
fflush(0);
execvp(array1[0], array1);
fprintf(stderr, "failed to execute '%s'\n", array1[0]);
return 1;

Multiple changes include:

  • Using sizeof to determine array sizes.
  • Changing spelling of "receive".
  • Subtracting 1 to allow space for a terminal null byte to convert messages to strings.
  • Making array1 big enough to hold a terminal NULL pointer and initializing (indirectly) the elements after the zeroth to NULL. This is important; the argument array to execvp() must be terminated with a NULL pointer.
  • Return if the initial receive fails, to avoid indexing by a negative number.
  • Making the buf1 array local to the loop; ditto bytes_received1.
  • Return if a subsequent receive fails, to avoid indexing by a negative number.
  • Making a copy of the strings read using strdup() — this is a key change.
  • Not trying to null-terminate buf1 at a position based on the data received in buf.
  • Revising the printing, putting square brackets around the string so trailing spaces or newlines can be spotted more easily.
  • Printing all the arguments to execvp().
  • Debating whether the debug output should go to stderr instead of stdout. I ended up leaving it going to stdout, but that isn't necessarily the best choice.
  • Changing one fprintf(stdout, …) to printf(…) for consistency.
  • Calling fflush(0) to send any pending output to its devices. With the debugging output going to stdout, if the output is piped to another program, the data will be fully buffered, not line buffered, and won't appear unless you force it to. Calling fflush(stdout) would also be an option. It's probable that you shouldn't (don't) have any file streams other than stdin, stdout, stderr open when you're calling execvp().
  • You should consider whether other streams (file descriptors) should be closed before reaching here, perhaps using the O_CLOEXEC or FC_CLOEXEC options to ensure that the file descriptors are closed on successful exec, so that the executed process doesn't get active file descriptors it doesn't know about.
  • Not bothering to check the return value from execvp(). If it returns, it failed; if it succeeds, it doesn't return.
  • Reporting an error message when execvp() fails to execute.
  • Leaving return 1; as part of the code after the failed execvp(). It would often be better to use exit(EXIT_FAILURE); or similar (_exit(EXIT_FAILURE) perhaps). Without the larger context of the function that calls this fragment of a function, it isn't possible to know what's best here.
  • Note that if execvp() fails and returns, rather than exits, you're leaking the memory allocated by strdup(). There should probably be a loop for (int i = 0; i < 3; i++) free(array1[i+1]); to release the copied memory before the return 1;.
  • The code doesn't check that there was no data truncation — it doesn't know if one of the recv() calls would have read more data than it did because there wasn't enough space to store all the data. You'd probably want to check that the actual data size is smaller than the space available to ensure that there wasn't truncation.
  • It isn't clear why the program name can be ten times bigger than the arguments. In general, arguments can be bigger than program names, though, since your sample data has arguments like 1, 2, 3, this is not a problem.
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278