0

I would like to pass a memory space to a callee to collect a string array which is filtered out from a file(i.e. /tmp/saveconfig, it is listed in the end of the question). Normally, it is supposed to have following output,

/dev/disk/by-id/scsi-2001b4d2039784462
/dev/disk/by-id/scsi-2001b4d2049798685
/dev/disk/by-id/scsi-2001b4d2032048753

More specificly, I assumed the caller, i.e. main(), just allocate a space by char *ptr = malloc(4096) and pass it to get_devs() and then free the space by caller. After organizing the array of strings in get_devs(), the caller can iterate the string from the allocated space by using char *dev[]. In other word, how can I correlate *ptr with *dev[]? But according to the snippet as below, I got segmentation fault. Where do I do wrong? Please give me a hint.

int get_devs(char *pdev[])
{
    FILE *fp;
    char str[64];
    int len = 0, count = 0;
    char *cp, *ptr;

    /* opening file for reading */
    fp = fopen("/tmp/saveconfig", "r");
    if(fp == NULL) {
        perror("Error opening file");
        return(-1);
    }
    while(fgets(str, 64, fp) != NULL) {
        if (ptr = strstr(str, "/dev/")) {
            for (len = 1, cp = ptr; *cp != '\n'; len++, cp++) {
                if (*cp == '"') {
                    *cp = '\0';
                    break;
                }
            }
            strncpy(*pdev, ptr, len);
            printf("%s\n", pdev);
            pdev++;
            count++;
        }
    }
    fclose(fp);
    return count;   
}

int main()
{
    int i = 0, count = 0;
    char *pdev[64];

    count = get_devs(pdev);
    for (; i < sizeof(pdev) / sizeof(*pdev); i++) {
        printf("%s\n", pdev[i]);
    }
}

The saveconfig file is as below,

{
  "fabric_modules": [], 
  "storage_objects": [
    {
      "attributes": {
        "block_size": 512, 
      }, 
      "dev": "/dev/disk/by-id/scsi-2001b4d2039784462", 
      "name": "scsi-2001b4d2039784462", 
      "plugin": "block", 
      "readonly": false, 
      "write_back": false
    }, 
    {
      "attributes": {
        "block_size": 4096
      }, 
      "dev": "/dev/disk/by-id/scsi-2001b4d2049798685", 
      "name": "scsi-2001b4d2049798685", 
      "plugin": "block", 
      "readonly": false, 
      "write_back": false
    }, 
    {
      "attributes": {
        "block_size": 512, 
      }, 
      "dev": "/dev/disk/by-id/scsi-2001b4d2032048753", 
      "name": "scsi-2001b4d2032048753", 
      "plugin": "block", 
      "readonly": false, 
      "write_back": false
    }
  ]
}
codexplorer
  • 541
  • 5
  • 21
  • 1
    `pdev` is just an array of uninitialised pointers - you need to allocate some memory for each string before you try and copy into it. – Paul R Apr 10 '17 at 17:00
  • @Paul R If I do *pdev = (char *)malloc(4096), it cause segmentation as well. – codexplorer Apr 10 '17 at 17:16
  • [Don't cast the result of `malloc`](http://stackoverflow.com/a/605858/1863564). That's not your problem, but it's worth mentioning. – Nic Apr 11 '17 at 01:08

2 Answers2

1

Many errors, but most importantly:

char *pdev[64];

Allocates space for 64 pointers-to-char. But you do not allocate any space for the characters themselves, so when you later strncpy(*pdev, ...), you're copying into random memory. Also, you're only using the first of the pointers. What you probably mean to do is something like:

char *pdev[1000];  // How many lines in file?

int i = 0;
while (fgets(str... {
    pdev[i] = malloc(strlen(str) + 1);
    memmove(pdev[i], str, strlen(str) + 1);
    . . .
    i += 1;
    . . .
Lee Daniel Crocker
  • 12,927
  • 3
  • 29
  • 55
0

Try this:

   ptr = strstr(str, "/dev/");

   if (!ptr) {
        .....

   }

strstr() returns NULL is there is no match.
Otherwise, it returns the address of the first character of the string of the first occurrence.

Nguai al
  • 958
  • 5
  • 15