4

I'm running linux and I try to do the following:

  1. Run ls on current directory (using popen)
  2. Output the result to buffer (using fread from pipe descriptor)
  3. close pipe (using pclose).

Everything works fine (the buffer is filled correctly with the ls result) but when I check pclose() result it returns -1 and errno is set to 10 (No child processes). Have no idea why this is happening but I can't ignore it (unless there is a reasonable explanation to why this is happening).

My code:

FILE * lsoutput = NULL;
lsoutput = popen("ls -ltr", "r");
if (readFromPipeOrFile(lsOutput, pSendBuf, pSendActualSize) == -1)
{
        printf("readFromPipeOrFile failed.");
        pclose(lsOutput);
        safeFree(pSendBuf);
        return -1;
}

if (pclose(lsOutput) == -1) // No idea why it returns -1 but it does...
{
        printf("pclose failed");
        printf("errno: %d\n", errno);
        printf("strerror: '%s'", strerror(errno));
        return -1;
}

The code for readFromPipeOrFile (the function writing to the buffer):

int readFromPipeOrFile(FILE * pipeOrFile, char ** pSendBuf, size_t * pSendActualSize)
{
     int multiplication = 1;
     char * pSendBufCurrentLocation = NULL;
     ERR_RETURN(pipeOrFile == NULL || pSendBuf == NULL || pSendActualSize == NULL,
                     "No values should be NULL.");
     ERR_RETURN(*pSendBuf != NULL, "*pSendBuf should be NULL");

     *pSendBuf = (char *) calloc (MAX_READ_FROM_STREAM * multiplication, sizeof(char));
     ERR_RETURN(*pSendBuf == NULL, "Failed allocating sendBuf");

     pSendBufCurrentLocation = *pSendBuf;
     while (fread(pSendBufCurrentLocation, MAX_READ_FROM_STREAM, 1, pipeOrFile) == 1)
     {
             ++multiplication;
             *pSendBuf = realloc(*pSendBuf, MAX_READ_FROM_STREAM * multiplication);
             ERR_RETURN(*pSendBuf == NULL, "Failed re-allocating sendBuf");

             pSendBufCurrentLocation = *pSendBuf + (MAX_READ_FROM_STREAM * (multiplication - 1));
             memset(pSendBufCurrentLocation, '\0', MAX_READ_FROM_STREAM);
     }

     ERR_RETURN(!feof(pipeOrFile), "Hasn't reached eof but fread stopped");
     ERR_RETURN(ferror(pipeOrFile), "Error in fread");

     *pSendActualSize = MAX_READ_FROM_STREAM * multiplication;

     return 0;
}

Thanks in advance! EDIT: ERR_RETURN is just a macro that checks if the condition on the first paramter is true and if so, print the string in the second parameter and return -1.

Ofer Levi
  • 41
  • 1
  • 4
  • Do you observe the same behaviour if `readFromPipeOrFile()` is not called? – alk Apr 14 '13 at 08:37
  • I copied the contents of readFromPipeOrFile() to where I originaly called it (minus the bound / NULL checks) and there is no change... – Ofer Levi Apr 14 '13 at 17:10
  • Was that meant as answer to my question? Did you just commented out the call to `readFromPipeOrFile()`, letting `popen()` be immediately followed by `pclose()`? – alk Apr 14 '13 at 17:34

2 Answers2

2

signal(SIGCHLD, SIG_IGN) will cause pclose() result it returns -1 and errno is set to 10 (No child processes).

ajaxhe
  • 571
  • 1
  • 5
  • 13
0

According to the documentation for pclose(), it returns -1 if wait4 returns an error. However, it says nothing of setting errno (other than setting ECHILD if it was unable to get a child status) so I'm not so sure you can count on the strerror() string being accurate.

The implication is that ls exited with an error code which would imply that ls had trouble accessing a subdirectory (permission error?). So, you have results because ls returned what it could read, but it is an incomplete read because it could not access a subdirectory in the recursion.

K Scott Piel
  • 4,320
  • 14
  • 19
  • I have compared the results of the ls in my code with ls i ran directly from the shell. I also did sudo just to make sure I hadn't missed anything... ls returns exactly what it should have. – Ofer Levi Apr 13 '13 at 22:51
  • Regarding wait4... I wonder if it is possible that the sub-process (created by popen) terminates before pclose is called? After all they are not synced. I haven't seen it written anywhere but is it possible? Thanks again... – Ofer Levi Apr 13 '13 at 22:53
  • In theory, when the `ls` terminates, it should hang around as a zombie... or at least the shell that executed it should and the `wait4` is supposed to behave like a `waitpid` in which case it should retrieve the exit status of the zombie and clean it up. There are caveats about the use of signal masks that would muck with that... are you masking any signals elsewhere in your code? Specifically `SIGCHILD` masked with `SIG_IGN` or `SA_NOCLDWAIT` is set. – K Scott Piel Apr 13 '13 at 23:14
  • Nope, no such thing. I'm not working with sub-processes in any other part of the code and i'm not masking signals... – Ofer Levi Apr 13 '13 at 23:56
  • 1
    You probably don't, but if you call wait from somewhere else that might be the problem. – Druesukker Apr 14 '13 at 00:02
  • I'm not calling wait.. not explicitly at least.. Does getline() call wait or anything like that when it waits for user input? I do use getline in a different session (this is a server client program, currently on the same computer in different sessions). – Ofer Levi Apr 14 '13 at 01:08