Read/Write struct to fifo in C - c

I'm trying to pass structs between processes using named pipes. I got stuck at trying to open the pipe non-blocking mode. Here's my code for writing to the fifo:
void writeUpdate() {
// Create fifo for writing updates:
strcpy(fifo_write, routing_table->routerName);
// Check if fifo exists:
if(access(fifo_write, F_OK) == -1 )
fd_write = mkfifo(fifo_write, 0777);
else if(access(fifo_write, F_OK) == 0) {
printf("writeUpdate: FIFO %s already exists\n", fifo_write);
//fd_write = open(fifo_write, O_WRONLY|O_NONBLOCK);
}
fd_write = open(fifo_write, O_WRONLY|O_NONBLOCK);
if(fd_write < 0)
perror("Create fifo error");
else {
int num_bytes = write(fd_write, routing_table, sizeof(routing_table));
if(num_bytes == 0)
printf("Nothing was written to FIFO %s\n", fifo_write);
printf("Wrote %d bytes. Sizeof struct: %d\n", num_bytes,sizeof(routing_table)+1);
}
close(fd_write);
}
routing_table is a pointer to my struct, it's allocated, so there's no prob with the name of the fifo or smth like that.
If I open the fifo without the O_NONBLOCK option, it writes smth for the first time, but then it blocks because I'm having troubles reading the struct too. And after the first time, the initial fifo is created, but other fifo's appear, named '.', '..'.
With O_NONBLOCK option set, it creates the fifo but always throws an error: 'No such device or address'. Any idea why this happens? Thanks.
EDIT: Ok, so I'm clear now about opening the fifo, but I have another problem, in fact reading/writing the struct to the fifo was my issue to start with. My code to read the struct:
void readUpdate() {
struct rttable *updateData;
allocate();
strcpy(fifo_read, routing_table->table[0].router);
// Check if fifo exists:
if(access(fifo_read, F_OK) == -1 )
fd_read = mkfifo(fifo_read, 777);
else if(access(fifo_read, F_OK) == 0) {
printf("ReadUpdate: FIFO %s already exists\n Reading from %s\n", fifo_read, fifo_read);
}
fd_read = open(fifo_read, O_RDONLY|O_NONBLOCK);
int num_bytes = read(fd_read, updateData, sizeof(updateData));
close(fd_read);
if(num_bytes > 0) {
if(updateData == NULL)
printf("Read data is null: yes");
else
printf("Read from fifo: %s %d\n", updateData->routerName, num_bytes);
int result = unlink(fifo_read);
if(result < 0)
perror("Unlink fifo error\n");
else {
printf("Unlinking successful for fifo %s\n", fifo_read);
printf("Updating table..\n");
//update(updateData);
print_table_update(updateData);
}
} else
printf("Nothing was read from FIFO %s\n", fifo_read);
}
It opens the fifo and tries to read, but it seems like nothing is in the fifo, although in writeUpdate the first time it says it wrote 4 bytes (this seems wrong too). At reading, first time around it prints 'a' and then num_bytes is always <=0.
I've looked around and only found this example, with simple write/read, is there smth more needed when writing a struct?
My struct looks like this:
typedef struct distance_table {
char dest[20]; //destination network
char router[20]; // via router..
int distance;
} distance_table;
typedef struct rttable {
char routerName[10];
char networkName[20];
struct distance_table table[50];
int nrRouters;
} rttable;
struct rttable *routing_table;

"No such device or address" is the ENXIO error message. If you look at the open man page, you'll see that this error is reported in particular if:
O_NONBLOCK | O_WRONLY is set, the named file is a FIFO and no process
has the file open for reading. (...)
which is exactly your situation. So the behavior you are seeing is normal: you can't write (without blocking) to a pipe that has no readers. The kernel won't buffer your messages if nothing is connected to the pipe for reading.
So make sure you start the "consumer(s)" before your "producer", or remove the non-blocking option on the producer.
BTW: using access is, in most circumstances, opening yourself to time of check to time of use issues. Don't use it. Try the mkfifo - if it works, you're good. If it fails with EEXISTS, you're good too. If it fails otherwise, clean up and bail out.
For the second part of your question, it really depends completely on how exactly the data you are trying to send is structured. Serializing a random struct in C is not easy at all, especially if it contains variable data (like char *s for example).
If you struct contains only primitive types (and no pointers), and both sides are on the same machine (and compiled with the same compiler), then a raw write on one side and read on the other of the whole struct should work.
You can look at C - Serialization techniques for more complex data types for example.
Concerning your specific example: you're getting mixed up between pointers to your structs and plain structs.
On the write side you have:
int num_bytes = write(fd_write, routing_table, sizeof(routing_table));
This is incorrect since routing_table is a pointer. You need:
int num_bytes = write(fd_write, routing_table, sizeof(*routing_table));
// or sizeof(struct rttable)
Same thing on the read side. On the receiving size you're also not allocating updateData as far as I can tell. You need to do that too (with malloc, and remember to free it).
struct rttable *updateData = malloc(sizeof(struct rrtable));

Related

Client does not read any data after reading another file

I am writing a simple client/server application and I ran into a very weird issue.
I am trying to send a .zip file to the client and some more data after that.
Sending the .zip works fine, the server writes to the socket, the client reads from the socket, just as intended.
The problem is right after that.
The server runs fine and keeps writing to the socket, but the client just won't read anything. It gets stuck on the very next read() call, no matter what I try to send.
I've checked to see if the socket descriptors are alright, and they are. I also thought that maybe there is not enough data in the socket for the client to read, but there definitely is.
I also tried doing the same write/read before and after sending that .zip file: it works fine before, but client doesn't see it after sending that .zip.
I am out of ideas.
Here's the function I use to send the .zip:
typedef struct thData{
int idThread; //thread ID
int cl; //client descriptor
}thData;
void send_info(struct thData tdL)
{
char file_path[256]="v1.zip";
char sd_buffer[256];
bzero(sd_buffer, 256);
FILE *fd = fopen(file_path, "rb");
if(fd == NULL)
{
printf("ERROR: %s not found.\n", file_path);
exit(1);
}
int read_size;
int write_size;
while((read_size = fread(sd_buffer, sizeof(char), 256, fd)) > 0)
{
if((write_size=write(tdL.cl, sd_buffer, read_size)) < 0)
{
perror("ERROR: writing to client: \n");
break;
}
bzero(sd_buffer, 256);
}
}
And what I use to receive the .zip:
void receive_info(int sd) //sd being the socket descriptor
{
char* file_path = "subject.zip";
char received_buffer[256];
int total_received=0;
int total_wrote=0;
FILE *fd = fopen(file_path, "wb");
if(fd == NULL)
printf("Cannot open %s\n", file_path);
else
{
bzero(received_buffer, 256);
int read_size = 0;
while((read_size = read(sd, received_buffer, 256)) > 0)
{
total_received=total_received+read_size;
int write_size = fwrite(received_buffer, sizeof(char), read_size, fd);
total_wrote=total_wrote+read_size;
if(write_size < read_size)
{
perror("ERROR: \n");
}
bzero(received_buffer, 256);
if (read_size == 0 || read_size != 256)
{
break;
}
}
if(read_size < 0)
{
perror("ERROR: reading: ");
exit(1);
}
fclose(fd);
}
}
Any help would be greatly appreciated.
I think that the problem is that you are reading too much.
In TCP there are no boundaries in the packets sent from one peer to the other. It is just a stream of bytes, and the pieces received from recv/read() bear no relation (in principle) to the pieces sent from send/write().
Now, imagine that your ZIP file is 300 bytes long, and your extra data is 10 bytes long. Your sender code will do:
write 256 bytes (first piece from ZIP).
write 44 bytes (last piece from ZIP).
write 10 bytes (the extra data).
And your receiver code will do:
read 256, get 256 bytes (first piece from ZIP).
read 256, get 54 bytes (last piece from ZIP plus the extra data).
read XXX bytes, wait forever!
If you look carefully to the ZIP files you'll probably see those extra bytes at the end of subject.zip.
The solution, if you don't want to close and open another socket, is to make the protocol a bit more complicated. For example, you could send a structure before the file (a header) that includes the size of the file. That way the receiver will know when to stop reading.
PS: Note that your code has a few risky edges. For example, write() might not write all the given bytes, but you are not checking that; you are not closing the file...
PS2: I find curious that you feel the need to write sizeof(char) instead of just 1 but you write 256 instead of sizeof(sd_buffer).

cat terminal , check usb removed (perror)?

For an assignment we have to create C program that functions similar to the cat command. The first hand-in requires it to mimic very minimal operations of cat....i.e print to output, redirect. The issue I'm having is that one requirement is to print an error in the case that an output file residing on a usb drive is lost, i.e usb pulled out whilst redirecting stdout to it.
How do I catch such an error, also how can perform a test-case for that particular error ??
Many Thanks....really have no idea
UPDATE CODE TEMP
int main(){
char c;
while((c = getchar()) != EOF){
putchar(c);
// Ensure newly created file exists
}
return EXIT_SUCCESS;
}
Assuming you are using fprintf(), from the man pages:
On success, the total number of characters written is returned.
So:
store the size of the char array you will write into a variable x
if fprintf() is less than x, the writing was interrupted.
exit gracefully
EDIT:
There are 2 things I'm thinking of:
1: When putchar() fails, it indicates an error when writing to the file. Since writing one byte doesn't take very long, this should be unlikely since it will be in a safe state once the byte is written (or you assume).
You can do this like so
if(putchar(c) == EOF){
//write error
}
2: If you're asked to quit the instant you detect a file removal, then you need to monitor the directory. Luckily, you're only looking at one directory. However that while loop gets in the way of things because getchar() is a blocking function (cannot return until something happens). You should use inotify to monitor the directory, then probably poll to poll the file descriptor of inotify(). When I did this I used select because we were forced to.
Some kind of an idea how to monitor a directory with inotify()
int length, i = 0;
char buffer[EVENT_BUF_LEN];
memset(buffer, 0, EVENT_BUF_LEN*sizeof(char));
//init inotify
fd = inotify_init();
if(fd < 0){
perror("inotify init");
}
//add directory to watch list
wd = inotify_add_watch(fd, path , IN_DELETE |
IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF | IN_MOVED_FROM | IN_MOVED_TO);
fd_set fds;
FD_ZERO(&fds);
FD_SET(fd, &fds);
//wait for event, since read() blocks
length = read( fd, buffer, EVENT_BUF_LEN );
if ( length < 0 ) {
perror("zero event length");
}
struct inotify_event *event;
while (i < length){
//cast the event to a char buffer
event = (struct inotify_event*) &buffer[i];
if (event->len){
//this was a custom function of mine
storeEvent(event);
}
i += EVENT_SIZE + event->len;
}
You'll have to check which attributes to use when adding a directory (like IN_DELETE or IN_MODIFY) since they will determine what triggers an inotify() event. Note this code will only detect one event, and blocks at the read() statement.

Fork Process / Read Write through pipe SLOW

ANSWER
https://stackoverflow.com/a/12507520/962890
it was so trivial.. args! but lots of good information received. thanks to everyone.
EDIT
link to github: https://github.com/MarkusPfundstein/stream_lame_testing
ORIGINAL POST
i have some questions regarding IPC through pipelines. My goal is to receive MP3 data per TCP/IP stream, pipe it through LAME to decode it to wav, do some math and store it on disk (as a wav). I am using non blocking IO for the whole thing.
What irritates me a bit is that the tcp/ip read is way more fast than the pipe line trough lame. When i send a ~3 MB mp3 the file gets read on the client side in a couple of seconds. In the beginning, i can also write to the stdin of the lame process, than it stops writing, it reads the rest of the mp3 and if its finished i can write to lame again. 4096 bytes take approx 1 second (to write and read from lame). This is pretty slow, because i want to decode my wav min 128kbs.
The OS Is a debian 2.6 kernel on a this micro computer:
https://www.olimex.com/dev/imx233-olinuxino-maxi.html
65 MB RAM
400 MhZ
ulimit -n | grep pipe returns 512 x 8 , means 4096 which is ok. Its a 32 bit system.
The weird thing is that
my_process | lame --decode --mp3input - output.wav
goes very fast.
Here is my fork_lame code (which shall essentialy connect stout of my process to stdin of lame and visa versa)
static char * const k_lame_args[] = {
"--decode",
"--mp3input",
"-",
"-",
NULL
};
static int
fork_lame()
{
int outfd[2];
int infd[2];
int npid;
pipe(outfd); /* Where the parent is going to write to */
pipe(infd); /* From where parent is going to read */
npid = fork();
if (npid == 0) {
close(STDOUT_FILENO);
close(STDIN_FILENO);
dup2(outfd[0], STDIN_FILENO);
dup2(infd[1], STDOUT_FILENO);
close(outfd[0]); /* Not required for the child */
close(outfd[1]);
close(infd[0]);
close(infd[1]);
if (execv("/usr/local/bin/lame", k_lame_args) == -1) {
perror("execv");
return 1;
}
} else {
s_lame_pid = npid;
close(outfd[0]); /* These are being used by the child */
close(infd[1]);
s_lame_fds[WRITE] = outfd[1];
s_lame_fds[READ] = infd[0];
}
return 0;
}
This are the read and write functions. Please not that in write_lame_in. when i write to stderr instead of s_lame_fds[WRITE], the output is nearly immedieatly so its definitly the pipe through lame. But why ?
static int
read_lame_out()
{
char buffer[READ_SIZE];
memset(buffer, 0, sizeof(buffer));
int i;
int br = read(s_lame_fds[READ], buffer, sizeof(buffer) - 1);
fprintf(stderr, "read %d bytes from lame out\n", br);
return br;
}
static int
write_lame_in()
{
int bytes_written;
//bytes_written = write(2, s_data_buf, s_data_len);
bytes_written = write(s_lame_fds[WRITE], s_data_buf, s_data_len);
if (bytes_written > 0) {
//fprintf(stderr, "%d bytes written\n", bytes_written);
s_data_len -= bytes_written;
fprintf(stderr, "data_len write: %d\n", s_data_len);
memmove(s_data_buf, s_data_buf + bytes_written, s_data_len);
if (s_data_len == 0) {
fprintf(stderr, "finished\n");
}
}
return bytes_written;
}
static int
read_tcp_socket(struct connection_s *connection)
{
char buffer[READ_SIZE];
int bytes_read;
bytes_read = connection_read(connection, buffer, sizeof(buffer)-1);
if (bytes_read > 0) {
//fprintf(stderr, "read %d bytes\n", bytes_read);
if (s_data_len + bytes_read > sizeof(s_data_buf)) {
fprintf(stderr, "BUFFER OVERFLOW\n");
return -1;
} else {
memcpy(s_data_buf + s_data_len,
buffer,
bytes_read);
s_data_len += bytes_read;
}
fprintf(stderr, "data_len: %d\n", s_data_len);
}
return bytes_read;
}
The select stuff is pretty basic select logic. All blocks are non blocking of course.
Anyone any idea? I'd really appreciate any help ;-)
Oops! Did you check your LAME output?
Looking at your code, in particular
static char * const k_lame_args[] = {
"--decode",
"--mp3input",
"-",
"-",
NULL
};
and
if (execv("/usr/local/bin/lame", k_lame_args) == -1) {
means you are accidentally omitting the --decode flag as it will be argv[0] for LAME, instead of the first argument (argv[1]). You should use
static char * const k_lame_args[] = {
/* argv[0] */ "lame",
/* argv[1] */ "--decode",
/* argv[2] */ "--mp3input",
/* argv[3] */ "-",
/* argv[4] */ "-",
NULL
};
instead.
I think you are seeing a slowdown because you're accidentally recompressing the MP3 audio. (I noticed this just a minute ago, so haven't checked if LAME does that if you omit the --decode flag, but I believe it does.)
It is possible there is some sort of a blocking issue wrt. nonblocking pipes (not really being nonblocking), causing your end to block until LAME consumes the data.
Could you try an alternative approach? Use normal, blocking pipes, and a separate thread (using pthreads), which has the singular purpose of writing data from a circular buffer to LAME. Your main thread then keeps filling the circular buffer from your TCP/IP connection, and can easily also track and report buffer levels -- very useful during development and debugging. I've had much better success with blocking pipes and threads than nonblocking pipes, in general.
In Linux, threads really do not have that much of an overhead, so you should be comfortable in using them even on embedded architectures. The only trick you must master is specifying a sensible stack size for the worker thread -- in this case 16384 bytes is quite likely enough -- because only the initial stack given to the process will automatically grow and threads stacks are fixed an by default quite large.
Do you need example code?
Edited to add:
Your program receives data from the TCP/IP connection probably at a steady rate. However, LAME consumes the data in largeish chunks. In other words, the situation is like a car being towed, with the tow car jerking and stopping, with the towee jerking into it every time: both your process and LAME are most of the time waiting the other to receive/send more data.
First, those two close are not required (actually, you shouldn't do that), because the two dup2 which follow will do it automatically :
close(STDOUT_FILENO);
close(STDIN_FILENO);

"Illegal seek" error when working with socket streams with non-empty read buffers

I'm currently writing a server application on Linux x86_64 using <sys/socket.h>.
After accepting a connection via accept(), I use fdopen() to wrap the retrieved socket into a FILE* stream.
Writing to, and reading from, that FILE* stream usually works quite well, but the socket becomes unsusable as soon as I write to it while it has a non-empty read buffer.
For demonstration purposes, I've written some code that listens for a connection, then reads the input, line by line, into a read buffer using fgetc(). If the line is too long to fit into the buffer, it's not completely read, but instead read during the next iteration.
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
FILE* listen_on_port(unsigned short port) {
int sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in name;
name.sin_family = AF_INET;
name.sin_port = htons(port);
name.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sock, (struct sockaddr*) &name, sizeof(name)) < 0)
perror("bind failed");
listen(sock, 5);
int newsock = accept(sock, 0, 0);
return fdopen(newsock, "r+");
}
int main(int argc, char** argv) {
int bufsize = 8;
char buf[9];
buf[8] = 0; //ensure null termination
int data;
int size;
//listen on the port specified in argv[1]
FILE* sock = listen_on_port(atoi(argv[1]));
puts("New connection incoming");
while(1) {
//read a single line
for(size = 0; size < bufsize; size++) {
data = fgetc(sock);
if(data == EOF)
break;
if(data == '\n') {
buf[size] = 0;
break;
}
buf[size] = (char) data;
}
//check if the read failed due to an EOF
if(data == EOF) {
perror("EOF: Connection reset by peer");
break;
} else {
printf("Input line: '%s'\n", buf);
}
//try to write ack
if(fputs("ack\n", sock) == EOF)
perror("sending 'ack' failed");
//try to flush
if(fflush(sock) == EOF)
perror("fflush failed");
}
puts("Connection closed");
}
The code should compile in gcc without any special parameters. Run it with the port number as argument and use netcat to connect to it locally.
Now, if you try sending strings that are shorter than 8 characters, this will run flawlessly.
But if you send a string containing more than 10 characters, the program will fail.
This sample input:
ab
cd
abcdefghij
Will create this output:
New connection incoming
Input line: 'ab'
Input line: 'cd'
Input line: 'abcdefgh'
fflush failed: Illegal seek
EOF: Connection reset by peer: Illegal seek
Connection closed
As you see, (rightly) only the first 8 characters of abcdefgh are read, but when the program tries to send the 'ack' string (which the client never receves), and then flush the output buffer, we receive an Illegal seek error, and the next call to fgetc() returns EOF.
If the fflush() part is commented out, the same error still occurs, but the
fflush failed: Illegal seek
line is missing from the server output.
If the fputs(ack) part is commented out, everything seems to work as intended, but a perror() manually called from gdb still reports an 'Illegal seek' error.
If both fputs(ack) and fflush() are commented out, everything does work as intended.
Unfortunately, I've not been able to find any good documentation, nor any Internet discussions on this problem, so your help would be greatly appreciated.
edit
The solution i finally settled for is to not use fdopen() and FILE*, since there seems to be no clean way of converting a socket fd into a FILE* that can reliably used in r+ mode.
Instead, I directly worked on the socket fd, writing my own replacement code for fputs and fprintf.
If anyone needs it, here is the code.
Clearly "r+" (read/write) mode does not work on sockets in this implementation, no doubt because the underlying code assumes that it must do a seek to switch between reading and writing. This is the general case with stdio streams (that you must do some kind of synchronizing operation), because back in the Dim Time, actual stdio implementations had only a single counter per stream, and it was either a counter of "number of characters left to read from stream buffer via getc macro" (in read mode) or "number of characters that can safely be written to stream buffer via putc macro (in write mode). To that that single counter re-set, you had to do a seek-type operation.
Seeks are not allowed on pipes and sockets (since "file offset" is not meaningful there).
One solution is not to wrap a socket with stdio at all. Another, probably easier / better for your purposes, is to wrap it with, not one, but two stdio streams:
FILE *in = fdopen(newsock, "r");
FILE *out = fdopen(newsock, "w");
There's another flaw here though, because when you go to fclose one stream, that closes the other's file descriptor. To work around that, you need to dup the socket descriptor once (in either of the two calls above, it does not matter which one).
If you intend to use select or poll or similar on the socket at some point, you should generally go for the "don't wrap with stdio" solution, since there's no nice clean portable way to track stdio buffering. (There are implementation-specific ways).
Don't use fflush() on network sockets. They are unbuffered streams.
Also, this code:
//read a single line
for(size = 0; size < bufsize; size++) {
data = fgetc(sock);
if(data == EOF)
break;
if(data == '\n') {
buf[size] = 0;
break;
}
buf[size] = (char) data;
}
does not read a single line. It only reads up to the buffer size, which you defined as 8. sock will still have data for you to receive which you should receive before writing to the stream with fputs. BTW you can replace that whole block with
fgets(buf, bufsize, sock);
Yes, you can use one file stream to handle your socket, at least on Linux.
But you should be careful with it: you must only use ferror() to test for errors. I have some code that use this and run flawlessly in production on a major French site.
If you use errno or perror() you'll catch any internal error that the stream will encounter, even if it wants to hide it to you. And "Illegal seek" is one of them.
Also, to test for real EOF conditions, you should use feof(), since when returning true it's mutually exclusive with ferror() returning a non-zero value. It's because, when using fgetc() you don't have any mean to differentiate error from real EOF conditions. So you should probably better use fgets() as another user pointed out.
So, your test:
if(data == EOF) {
perror("EOF: Connection reset by peer");
break;
} else {
printf("Input line: '%s'\n", buf);
}
Should be written as:
int sock_error = ferror(sock);
if (sock_error) {
fprintf(stderr, "Error while reading: %s", strerror(sock_error));
} else {
printf("Input line: '%s'\n", buf);
}
Try this :
#define BUFSIZE 88
FILE* listen_on_port(unsigned short port) {
...
}
int main(int argc, char** argv) {
int bufsize = BUFSIZE;
char buf[ BUFSIZE ];

What is wrong with this _popen / select example?

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.
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.
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.
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.
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.
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);
The first FD_ISSET(...) should be on the fd_set fd.
if (FD_ISSET(filePointer, &fd))
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);
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.
}
}
since select doesn't work i used threads, specifically _beginthread , _beginthreadex.

Resources