tags:

views:

1306

answers:

7

UPDATE: i updated the code and problem description to reflect my changes.

I know now that i'm trying a Socket operation on nonsocket. or that my fd_set is not valid since:

select returns -1 and WSAGetLastError()returns 10038.

But i can't seem to figure out what it is. Platform is Windows. I have not posted the WSAStartup part.

int loop = 0;
FILE *output

int main()
{
    fd_set fd;
    output = _popen("tail -f test.txt","r");

    while(forceExit == 0)
    {   
        FD_ZERO(&fd);
        FD_SET(_fileno(output),&fd);

        int returncode = select(_fileno(output)+1,&fd,NULL,NULL,NULL);
        if(returncode == 0)
        {
            printf("timed out");
        }
        else if (returncode < 0)
        {
            printf("returncode: %d\n",returncode);
            printf("Last Error: %d\n",WSAGetLastError());
        }
        else
        {
            if(FD_ISSET(_fileno(output),&fd))
            {
                if(fgets(buff, sizeof(buff), output) != NULL )
                {      
                    printf("Output: %s\n", buff);
                }
            }
            else
            {
                printf(".");
            }
        }
        Sleep(500);
    }
    return 0;
}

The new outcome now is of course the print out of the returncode and the last error.

A: 

The first argument to select needs to be the highest-numbered file descriptor in any of the three sets, plus 1:

   int select(int nfds, fd_set *readfds, fd_set *writefds,
              fd_set *exceptfds, struct timeval *timeout);

Also:

    if(FD_ISSET(filePointer,&exceptfds))
    {
            printf("i have data\n");
    }

Should be:

    if(FD_ISSET(filePointer,&fd))
    {
            printf("i have data\n");
    }

You should check the return code from select().

You also need to reset the fdsets each time you call select().

You don't need timeout since you're not using it.

Edit:

Apparently on Windows, nfds is ignored, but should probably be set correctly, just so the code is more portable.

If you want to use a timeout, you need to pass it into the select call as the last argument:

// Reset fd, exceptfds, and timeout before each select()...
int result = select(maxFDPlusOne, &fd, NULL, &exceptfds, &timeout);

if (result == 0)
{
    // timeout
}
else if (result < 0)
{
    // error
}
else
{
    // something happened
    if (FD_ISSET(filePointer,&fd))
    {
        // Need to read the data, otherwise you'll get notified each time.
    }
}
Douglas Leeder
Your comment about the first argument to select needing to be set correctly is true for Berkeley sockets. it is not true in Windows. The parameter is unused (although it should still be set correctly IMHO!). Poseter didn't specify platform so I thought I'd point out the different in platforms.
Mark
platform is indeed Windows. Sorry for not posting that
SinisterDex
A: 

The first thing that I notice is wrong is that you are calling FD_ISSET on your exceptfds in each conditional. I think that you want something like this:

if (FD_ISSET(filePointer,&fd))
{
    printf("i have data\n");
}
else ....

The except field in the select is typically used to report errors or out-of-band data on a socket. When one of the descriptors of your exception is set, it doesn't mean an error necessarily, but rather some "message" (i.e. out-of-band data). I suspect that for your application, you can probably get by without putting your file descriptor inside of an exception set. If you truly want to check for errors, you need to be checking the return value of select and doing something if it returns -1 (or SOCKET_ERROR on Windows). I'm not sure of your platform so I can't be more specific about the return code.

Mark
+1  A: 

You have some data ready to be read, but you are not actually reading anything. When you poll the descriptor next time, the data will still be there. Drain the pipe before you continue to poll.

Alex B
A: 
  1. select() first argument is the highest number file descriptor in your set, plus 1. (i.e. output+1)

    select(output+1, &fd, NULL, &exceptfds, NULL);

  2. The first FD_ISSET(...) should be on the fd_set fd.

    if (FD_ISSET(filePointer, &fd))

  3. Your data stream has data, then you need to read that data stream. Use fgets(...) or similar to read from the data source.

    char buf[1024]; ... fgets(buf, sizeof(buf) * sizeof(char), output);

Phil
changes reflect your suggestions, select throws a -1 though.
SinisterDex
+1  A: 

As far as I can tell, Windows anonymous pipes cannot be used with non-blocking calls like select. So, while your _popen and select code looks good independently, you can't join the two together.

Here's a similar thread elsewhere.

It's possible that calling SetNamedPipeHandleState with the PIPE_NOWAIT flag might work for you, but MSDN is more than a little cryptic on the subject.

So, I think you need to look at other ways of achieving this. I'd suggest having the reading in a separate thread, and use normal blocking I/O.

Roddy
ok.. i tried your approach to no avail. Do you have any idea how i could achieve a non-blocking fgets ?
SinisterDex
You can't, AFAICT. Your best bet is to simply use another thread for the reading, and do normal blocking IO.
Roddy
A: 

since select doesn't work i used threads, specifically _beginthread , _beginthreadex.

SinisterDex
+1  A: 

First of all, as yourself and others have pointed out, select() is only valid for sockets under Windows. select() does not work on streams which is what _popen() returns. Error 10038 clearly identifies this.

I don't get what the purpose of your example is. If you simply want to spawn a process and collect it's stdout, just do this (which comes directly from the MSDN _popen page):

int main( void )
{

   char   psBuffer[128];
   FILE   *pPipe;

   if( (pPipe = _popen("tail -f test.txt", "rt" )) == NULL )
      exit( 1 );

   /* Read pipe until end of file, or an error occurs. */

   while(fgets(psBuffer, 128, pPipe))
   {
      printf(psBuffer);
   }


   /* Close pipe and print return value of pPipe. */
   if (feof( pPipe))
   {
     printf( "\nProcess returned %d\n", _pclose( pPipe ) );
   }
   else
   {
     printf( "Error: Failed to read the pipe to the end.\n");
   }
}

That's it. No select required.

And I'm not sure how threads will help you here, this will just complicate your problem.

mhawke