0

i'm using a given md5 function who calculates a file hash when you feed it with a file address. Thing is that i need to execute this program using fork() and then load it using any exe...() function(im trying with execlp()) but when i do it and i pass the single argument i need to calculate the hash it fails. I tried running md5 program manually with the exact argument i use in execlp and it doesn't fail so i'm just assuming it must be something wrong with execlp parameters. Here's an example i made to explain the scenario:

#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(){
  pid_t cpid;int status;
  cpid = fork();
  if(cpid < 0){
    printf("Error fork\n");
    exit (EXIT_FAILURE);
  }else if (!cpid){
        if (execlp("./hashMD5/me/md5","md5","testfile.a",(char*)NULL) == -1){
          printf("Error: Loading process\n");
          exit(EXIT_FAILURE);
        }
  }else{
    waitpid(cpid,&status,0);
  }
    exit (EXIT_SUCCESS);
}

when i use this i got an error in terminal:

$testfile.a can't be opened

but if i manually execute the md5 program with the exactly same argument i got the correct execution.

What's wrong? Help!

Frank Ponte
  • 135
  • 1
  • 11

2 Answers2

2

the following proposed code:

  1. cleanly compiles
  2. documents why each header file is included
  3. uses a proper call to execl() rather than execlp() because execl() expects the first parameter to be a complete path while execlp() expects the first parameter to be just a file name.
  4. properly formats the code, for ease of readability and understanding
  5. properly handles calling execl() and possible failure of that call
  6. properly passes error messages to stderr rather than stdout, using perror(), so the reason the system thinks the error occurred is also displayed on stderr.

And now, the proposed code:

#include <stdio.h>   // perror()
#include <sys/types.h>
#include <stdlib.h>   // exit(), EXIT_FAILURE, EXIT_SUCCESS
#include <unistd.h>   // fork(), execlp()
#include <sys/wait.h> // waitpid()

int main( void )
{
    pid_t cpid;int status;
    cpid = fork();

    if(cpid < 0)
    { // error
        perror("Error fork\n");
        exit (EXIT_FAILURE);
    }

    else if (!cpid)
    { // child
        execl("./hashMD5/me/md5","md5","testfile.a", NULL);

        perror("Error: Loading process\n");
        exit(EXIT_FAILURE);
    }

    else
    { // parent
        waitpid(cpid,&status,0);
    }

    exit (EXIT_SUCCESS);
}
user3629249
  • 16,402
  • 1
  • 16
  • 17
  • It doesn't document why `` is included. POSIX 2004 did away with the need to include `` explicitly (though it was theoretically needed in POSIX 1997). Also note that in theory, you should cast the `NULL` to `(char *)NULL`. Whether you get away without the cast depends on the definition of `NULL`. – Jonathan Leffler Nov 26 '17 at 20:44
  • @JonathanLeffler, the final `NULL` parameter can also be left off the call. The `sys/types.h` header file is included because some (older) C support headers failed to include it in their definition. Since it is not directly related to any objects in the OPs code, there is no clear reason for its inclusion. However, we have no guarantee that everyone is using an up-to-date compiler. Especially if using Visual Studio or Visual C. – user3629249 Nov 26 '17 at 20:53
  • No; the NULL cannot be left off the calls to `execl*()` functions. It marks the end of the argument list and is crucial, and must be a null character pointer, which is why the cast is necessary on some machines (e.g. a 64-bit system where `#define NULL 0` is used instead of `#define NULL ((void *)0)`). And I documented that older means 'archaic' as in "prior to POSIX 2004", which is itself very old these days. You claim to have documented why each header is included; you didn't annotate why `` was included. It's up to you; you can include it and add a commentary about why, or … – Jonathan Leffler Nov 26 '17 at 20:56
  • @JonathanLeffler, Here are the prototypes for `execl()` and `execlp()`, from the linux MAN page for those functions: `int execl(const char *path, const char *arg, ... /* (char *) NULL */); int execlp(const char *file, const char *arg, ... /* (char *) NULL */);` Which clearly indicates the NULL argument is optional – user3629249 Nov 26 '17 at 21:18
  • 1
    No; it clearly indicates that the NULL argument is mandatory, but cannot be specified as mandatory using the `...` notation because that says "all bets are off about what goes here". – Jonathan Leffler Nov 26 '17 at 21:21
  • 1
    Note that the POSIX description of [`execl()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/execl.html) includes: _`int execl(const char *path, const char *arg0, ... /*, (char *)0 */);`_ and _The arguments represented by `arg0,...` are pointers to null-terminated character strings. These strings shall constitute the argument list available to the new process image. **The list is terminated by a null pointer.** The argument `arg0` should point to a filename string that is associated with the process being started by one of the `exec` functions_ (emphasis added). – Jonathan Leffler Nov 26 '17 at 21:29
0

I finally solved the problem. I appreciate the improvements people gave me, i'm always happy to learn new things!

The problem was with the argument itself: Even when you use execlp to create a brand new process the path of the argument remains relative to parent process, that's why was not working. After several headaches i finally realized that. Thanks to everyone!

Frank Ponte
  • 135
  • 1
  • 11
  • You mean that the `testfile.a` file was in the `hashMD5/me` directory, not in the directory containing that path? Yes, all relative paths are interpreted relative to the current directory of the process, which is the current directory of the parent process unless the program does an exlicit `chdir()` operation to change directory. – Jonathan Leffler Nov 26 '17 at 21:24
  • exactly, @JonathanLeffler. Oh, im gonna read about that function, awesome! – Frank Ponte Nov 27 '17 at 21:48