2

Im trying to use the md5sum command in a C program, right now im using dirent.h to get all the files in a folder, now, I want to get all the md5 of all those files, I am doing this:

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <dirent.h>

int main(void){

  char *word = ".gz";
  int i=0;
  char *word2 = ".";
  char *word3 = "..";
  unsigned int md5;
  DIR           *d;
  struct dirent *dir;
  d = opendir(".");
  if (d)  {

    while ((dir = readdir(d)) != NULL)
    {
      if((strstr(dir->d_name, word) == NULL) && (strcmp(dir->d_name, word2) != 0) && (strcmp(dir->d_name, word3)!= 0)) {
      md5 = system("md5sum dir->d_name");
      printf("The md5 of %s is %d\n", dir->d_name, md5);
      }
    }
  }
  return(0);
}

but when I run it, it says, for example:

md5sum: dir-: No such file or directory
The md5 of ej1_signal.c is 256
md5sum: dir-: No such file or directory
The md5 of pipeL.c is 256

Could you please explain me why is this happening? Thanks !

Hook
  • 391
  • 5
  • 16
  • 3
    I strongly suggest reading the document of the [`system`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/system.html) function first. – Cristian Ciupitu Dec 27 '14 at 16:10
  • 3
    do you realize that system is not going to return the `md5` value, but the exit code of the command? – Iharob Al Asimi Dec 27 '14 at 16:10
  • 3
    Am I the only one saying "wat" on `"md5sum dir->d_name"`? – E_net4 Dec 27 '14 at 16:19
  • 1
    i'd HIGHLY recommend using `shell script` for these purposes, parsing/executing shell-commands via standard C is .... error-prone at best, you will pretty much need to be an expert in both languages. Always use the tool which fits best, everything else will create headaches, even if someone helps you. – specializt Dec 27 '14 at 16:23
  • try using `snprintf` to combine "md5sum %s" with the actual name of the file ... and you may want `popen()` instead of `system()` if you are on a Posix system ... not sure the win32 alternative – technosaurus Dec 27 '14 at 16:27
  • I modified your code and posted an answer, there I explain how to do it, and why. – Iharob Al Asimi Dec 27 '14 at 16:42

4 Answers4

3

system does not return the output of a command. To get the output of a command, you need to create a process and tie the standard output stream to a file descriptor you can read data off in the other process. For an example on how to do that, you can refer to the pipe man page (section 2).

Another option is to use a library that provides an MD5 implementation (eg. OpenSSL). The man page of EVP_DigestInit (section 3) provides an example for that.

Another problem is that your code tries to calculate the digest of d->d_name, not the file which name is in d->d_name. You could use sprintf or strncat with a suitably sized buffer (ie. the length of the static string part md5sum plus the maximum size of the file name (usually 256 bytes, may vary between library implementations and file systems) plus another byte for safely terminating the string (as some implementations may report an unterminated string in d->d_name)). Please note that this does not apply if you use a library for digest calculation, as the library uses either the file name or you need to pass the file contents to a library function (eg. EVP_DigestUpdate).

Abrixas2
  • 3,207
  • 1
  • 20
  • 22
3

The system function doesn't returns you what you think. system is used to launch a command and when that command finished, it (generally) exits with an exit code. This is the value you catched.

What you need is the output of the command not its return value. So what you need is popen which lets you launch some external command and read/write to it through a pipe. See http://pubs.opengroup.org/onlinepubs/009695399/functions/popen.html for example.

Jean-Baptiste Yunès
  • 34,548
  • 4
  • 48
  • 69
  • Hey thanks, didn't know that, the system thing i read it on a forum and i thought it would work, i did it with the popen, thanks :) – Hook Dec 27 '14 at 16:32
2

The first problem is that you launch a new shell process executing "md5sum dir->d_name", meaning it does a md5 on the "file" named dir->d_name, instead of using the value you get from readdir.

So you could add a temp variable, and prepare the command in it prior to running system.

limits.h is for Linux, adjust it if necessary to get the max length of a path

...
#include <linux/limits.h>

char temp[PATH_MAX];

then instead of

md5 = system("md5sum dir->d_name");

add

strcpy(temp, "md5sum ");
strcat(temp, dir->d_name);
system(temp);

as for the other problem (system will not return the md5 string), this will display the md5 of the file in the directory. And you can just remove the printf ...

Déjà vu
  • 28,223
  • 6
  • 72
  • 100
1

There is no command in C to return the output of an external command, but there exists popen you can just open a command as a FILE * and read the output from it. This is how you can do it, and it's all explained within the code

#include <sys/types.h>
#include <sys/stat.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <dirent.h>

int main(void)
{
    DIR           *d;
    struct dirent *dir;

    d = opendir(".");
    if (d == NULL)
        return -1;

    while ((dir = readdir(d)) != NULL)
    {
        char        command[sizeof dir->d_name + 10];
        struct stat st;
        FILE       *pipe;

        if (stat(dir->d_name, &st) == -1)
            continue;
        /* check if the entry is a directory, md5sum does not work with them */
        if (S_ISDIR(st.st_mode) != 0)
            continue;
        /*
         * md5sum dir->d_name will pass `dir->d_name` as the argument to the md5sum command,
         * we need to build the command string, I like snprintf in this case
         */
        snprintf(command, sizeof command, "md5sum \"%s\"", dir->d_name);

        /*
         * Open the pipe, it will execute the new command in a new process (fork)
         * and create a pipe for communication with the current porcess
         */
        pipe = popen(command, "r");
        if (pipe != NULL)
        {
            char md5[33];

            /* read the md5 digest string from the command output */
            fread(md5, 1, sizeof md5 - 1, pipe);
            /* append a null terminator */
            md5[sizeof md5 - 1] = '\0';

            printf("The md5 of %s is %s\n", dir->d_name, md5);
        }
        /* close the pipe */
        pclose(pipe);
    }
    /* you should always call closedir() if opendir() succeded */
    closedir(d);

    return 0;
}
Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97