Unix Programming - select + stderr

This is Interesting: Free IT Magazines  
Home > Archive > Unix Programming > January 2005 > select + stderr





You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

Author select + stderr
Thomas Beneke

2005-01-21, 2:51 am

Hi,

I have a client - server, but this works only for stdout. What do I have
to change . If I send a 'ls' to the server, I get the correct response
on the client side. If I send a 'ls /sdfdsfdlfsdl' ( a non existing
file), I get the error message on server side on stderr, but on client
side on stdout.

while (FD_ISSET(sockfd, &rfdset))
{
if (select( fd+1 , &rfdset, NULL, NULL, NULL) < 0) {
if (errno != EINTR) {
fprintf(stderr,"select failed\n");
exit(1);
}
}
if (FD_ISSET(sockfd, &rfdset)) {
if ( (numbytes = recv(sockfd, buffer, sizeof(buf), MSG_WAITALL))
< 0 )
{
fprintf(stderr,"recv failed.\n");
exit(1);
}
if ( numbytes ) {
if ( (write(STDOUT_FILENO, buffer, numbytes)) <0) {
fprintf(stderr,"send stdout failed\n");
exit(1);
}
}
else if (numbytes == 0 || errno != EWOULDBLOCK)
FD_CLR(sockfd, &rfdset);
}
}
Jens.Toerring@physik.fu-berlin.de

2005-01-21, 7:48 am

Thomas Beneke <thomas.beneke@web.de> wrote:
> I have a client - server, but this works only for stdout.


What works? What means stdout in the context of a server-client pair?

> What do I have
> to change . If I send a 'ls' to the server, I get the correct response
> on the client side. If I send a 'ls /sdfdsfdlfsdl' ( a non existing
> file), I get the error message on server side on stderr, but on client
> side on stdout.


> while (FD_ISSET(sockfd, &rfdset))


I don't understand what this is good for - using the FD_ISSET macro
only really makes sense *after* you called select().

> {
> if (select( fd+1 , &rfdset, NULL, NULL, NULL) < 0) {
> if (errno != EINTR) {
> fprintf(stderr,"select failed\n");
> exit(1);
> }
> }
> if (FD_ISSET(sockfd, &rfdset)) {
> if ( (numbytes = recv(sockfd, buffer, sizeof(buf), MSG_WAITALL))
> < 0 )


Are you sure the MSG_WAITALL is appropriate here? What is supposd to
happen when the message sent by the server is less than sizeof(buf)
bytes?

> {
> fprintf(stderr,"recv failed.\n");
> exit(1);
> }
> if ( numbytes ) {
> if ( (write(STDOUT_FILENO, buffer, numbytes)) <0) {
> fprintf(stderr,"send stdout failed\n");
> exit(1);
> }
> }
> else if (numbytes == 0 || errno != EWOULDBLOCK)


If recv() didn't return -1 the value of errno doesn't tell you anything,
you are never supposed to do anything with errno's value unless an error
happened. If you feel like checking for EWOULDBLOCK (BTW, it could also
be EAGAIN according to SUSv3) you need to do that further up where you
check 'numbytes' for a negative value.

> FD_CLR(sockfd, &rfdset);


If you call select() with a cleared fdset you tell it to wait for *no*
file descriptors to become ready for reading. I guess you do that because
of the bogus while loop condition at the start. You better make the while
loop an infinite loop (i.e. use "while(1)" and break from it once you find
that you recv() returned 0 (telling you that the other side closed the
socket).

> }


Before you call select() again you must set up the fdset anew - other-
wise the case that select() did return -1 with errno being set to EINTR
won't be dealt with correctly (or if you're dealing with more than a
single file descriptor you call select() on).

> }


If I guess correctly that you want to have your client send a shell
command to the server, have the server execute it by fork()ing and
exec()ing a shell and send back both what the server received from
both the stdout and stderr of the shell it spawned separately, then
you need two logical channels between the server and the client -
one for sending back the result from stdout and one for sending what
appeared on stderr. For that you need either two sockets or you must
develop some kind of protocol that allows the client to figure out
what was what. There's nothing magical about stdout and stderr, they
are just two different file descriptor - and when you have the server
send what it read from both these channels intermixed via a single
channel to the client, the client won't be able to untangle them.

The simplest solution is probably to open two sockets and have the
server write what it got from the stdout of the shell via one of the
sockets and what it read from stderr of the shell to the other one.
The client then listens on both these sockets, writing out what it
receives from the first one to its stdout and what it receives from
the second one to its stderr.
Regards, Jens

PS: I hope you're aware of the security implications of such a server
running on your machine and have access to that server secured by a
password (which you better don't send unencrypted over the net!).
--
\ Jens Thoms Toerring ___ Jens.Toerring@physik.fu-berlin.de
\__________________________ http://www.toerring.de
Jens.Toerring@physik.fu-berlin.de

2005-01-21, 5:52 pm

Thomas Beneke <thomas.beneke@web.de> wrote:
> Now I've added on client a second socket()


> errfd = socket(AF_INET, SOCK_STREAM, 0)


> a second connect()


> connect(errfd, (struct sockaddr *)&server, sizeof(struct sockaddr))


> and a FD_ISSET for errfd


> if (FD_ISSET(errfd, &rfdset)) {
> if ( (numbytes = read(errfd, buffer, sizeof(buf))) <= 0 )
> .


> On my server i've added a second getpeername()


> getpeername(err_fd, (struct sockaddr *)&client, &client_len)


No idea what you need that for...

> the pipe(), fork() and exec()


> if (pipe(pipe_out) == -1 || pipe(pipe_err) == -1 )
> exit;


> switch (fork()) {
> case -1:
> fprintf(stderr, "fork failed.\n");
> _exit(1);


> case 0:
> /* child */


> /* stdout. */
> close(pipe_out[0]);
> if (dup2(pipe_out[1], 1) == -1)


It's probably better to write 'STDOUT_FILENO' instead of using the
number 1 here since it's easier to recognnize.

> fprintf(stderr, "aasapd: dup2(pipe_out).\n");
> close(pipe_out[1]);


> /* stderr. */
> close(pipe_err[0]);
> if (dup2(pipe_err[1], 2) == -1)


And here 'STDERR_FILENO'.

> fprintf(stderr, "aasapd: dup2(pipe_err).\n");
> close(pipe_err[1]);


> execl("/bin/sh", "sh", "-c", buffer, (char *) 0);
> _exit(1);
> default:
> default:
> /* parent */
> close(pipe_out[1]);
> close(pipe_err[1]);
> }


> /* only for test */
> numbytes = read(pipe_err[0], buffer, sizeof(buf));
> write(err_fd, buffer, numbytes);
> numbytes = read(pipe_out[0], buffer, sizeof(buf));
> write(client_fd, buffer, numbytes);


> The Problem now is that both outputs using stdin on my client. Why ?


What does that sentence mean? What is "both" and what means "outputs
using stdin"? On the client side what you get from client_fd should
be the stdout of the command the server did run for you and from
err_fd what it got from stderr. If you write that out in your client
you of course have to write what you got from client_fd to the clients
stdout and what you got from err_fd to its stderr file descriptor.

Regards, Jens
--
\ Jens Thoms Toerring ___ Jens.Toerring@physik.fu-berlin.de
\__________________________ http://www.toerring.de
Thomas Beneke

2005-01-21, 5:52 pm

Jens.Toerring@physik.fu-berlin.de wrote:
> Thomas Beneke <thomas.beneke@web.de> wrote:
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
> No idea what you need that for...
>
>
>
>
>
>
>
>
>
>
>
>
> It's probably better to write 'STDOUT_FILENO' instead of using the
> number 1 here since it's easier to recognnize.
>
>
>
>
>
>
> And here 'STDERR_FILENO'.
>
>
>
>
>
>
>
>
>
>
> What does that sentence mean? What is "both" and what means "outputs
> using stdin"? On the client side what you get from client_fd should
> be the stdout of the command the server did run for you and from
> err_fd what it got from stderr. If you write that out in your client
> you of course have to write what you got from client_fd to the clients
> stdout and what you got from err_fd to its stderr file descriptor.
>
> Regards, Jens

Hi Jens,

if I trace the server I see the normal output is send to 1(STDOUT) and
the error message is send to 2(STDERR). But on the client I get both on
the same fd in my select().

Regards,
Thomas
Jens.Toerring@physik.fu-berlin.de

2005-01-21, 5:52 pm

Thomas Beneke <thomas.beneke@web.de> wrote:
> if I trace the server I see the normal output is send to 1(STDOUT) and
> the error message is send to 2(STDERR).


Now you have me completely confused. As far as I understood until now
you intended to write what the shell did pass via its stdout to the
server through one socket to the client and what the server got from
the stderr of the shell would be copied to the other socket. Now you
suddenly write the server is sending stuff to stdout and stderr and
there's no mentioning of sockets anymore...

> But on the client I get both on the same fd in my select().


And now we're back to sockets... Do you use different ports for
the socket you send the stdout stuff to and for the other one
via which you send the stderr data?

Regards, Jens
--
\ Jens Thoms Toerring ___ Jens.Toerring@physik.fu-berlin.de
\__________________________ http://www.toerring.de
Thomas Beneke

2005-01-21, 5:52 pm

Jens.Toerring@physik.fu-berlin.de wrote:
> Thomas Beneke <thomas.beneke@web.de> wrote:
>
>
>
> Now you have me completely confused. As far as I understood until now
> you intended to write what the shell did pass via its stdout to the
> server through one socket to the client and what the server got from
> the stderr of the shell would be copied to the other socket. Now you
> suddenly write the server is sending stuff to stdout and stderr and
> there's no mentioning of sockets anymore...
>
>
>
>
> And now we're back to sockets... Do you use different ports for
> the socket you send the stdout stuff to and for the other one
> via which you send the stderr data?
>
> Regards, Jens

Hi Jens,

sorry about the confusion. I use the same ports for both directions.

everthing is fine ('ls /root')
<SNIP>
3927 select(5, [3 4], NULL, NULL, NULL) = 1 (in [3])
3927 read(3,
"Desktop\nblfs.txt\nc_samples\ngew_bereitschaft.txt\ngew_route.sh\nlinux_disk.txt\nminicom.log\nplatin.ini\nsapgui.png\nsapmsg.ini\nsaproute.ini\nstaroffice7\nwebsite\n",
1024) = 154
3927 write(1,
"Desktop\nblfs.txt\nc_samples\ngew_bereitschaft.txt\ngew_route.sh\nlinux_disk.txt\nminicom.log\nplatin.ini\nsapgui.png\nsapmsg.ini\nsaproute.ini\nstaroffice7\nwebsite\n",
154) = 154
3927 select(5, [3 4], NULL, NULL, NULL) = 1 (in [3])
3927 read(3, "", 1024) = 0
3927 close(3) = 0
3927 close(4) = 0
</SNIP>

something wrong ('ls /dfdddfd' #a non existing file)
<SNIP>
3934 select(5, [3 4], NULL, NULL, NULL) = 1 (in [3])
3934 read(3, "ls: ", 1024) = 4
3934 write(1, "ls: ", 4) = 4
3934 select(5, [3 4], NULL, NULL, NULL) = 1 (in [3])
3934 read(3, "", 1024) = 0
3934 close(3) = 0
3934 close(4) = 0
</SNIP>

The output of the second trace reads the client from fd 3 (STDOUT from
server) instead of fd 4 (STDERR from server).

--------------------------------------------------

The trace on the server
for ('ls')
3968 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS,
-1, 0) = 0xb7e92000
3968 write(1,
"Desktop\nblfs.txt\nc_samples\ngew_bereitschaft.txt\ngew_route.sh\nlinux_disk.txt\nminicom.log\
nplatin.ini\nsapgui.png\nsapmsg.ini\nsaproute.ini\nstaroffice7\nwebsite\n",
154) = 154
3968 close(1) = 0
3968 munmap(0xb7e92000, 4096) = 0
3968 exit_group(0) = ?
3966 <... read resumed> "", 1024) = 0
3966 write(2, "", 0) = 0
3966 read(4,
"Desktop\nblfs.txt\nc_samples\ngew_bereitschaft.txt\ngew_route.sh\nlinux_disk.txt\nminicom.log\n
platin.ini\nsapgui.png\nsapmsg.ini\nsaproute.ini\nstaroffice7\nwebsite\n",
1024) = 154
3966 write(0,
"Desktop\nblfs.txt\nc_samples\ngew_bereitschaft.txt\ngew_route.sh\nlinux_disk.txt\nminicom.log\
nplatin.ini\nsapgui.png\nsapmsg.ini\nsaproute.ini\nstaroffice7\nwebsite\n",
154) = 154
3966 wait4(-1, NULL, 0, NULL) = 3968

for ('ls /ddddddd')3976 write(2, "ls: ", 4 <unfinished ...>
3974 <... read resumed> "ls: ", 1024) = 4
3974 write(2, "ls: ", 4) = 4
3974 read(4, <unfinished ...>
3976 <... write resumed> ) = 4
3976 write(2, "/ddddddd", 8) = 8
3976 write(2, ": No such file or directory", 27) = 27
3976 write(2, "\n", 1) = 1
3976 exit_group(1) = ?
3974 <... read resumed> "", 1024) = 0
3974 write(0, "", 0) = 0

And here goes something wrong, but I dont no why.

Regards,
Thomas



Jens.Toerring@physik.fu-berlin.de

2005-01-21, 5:52 pm

Thomas Beneke <thomas.beneke@web.de> wrote:
> sorry about the confusion. I use the same ports for both directions.


I didn't ask about "directions", I asked if you use two sockets with
_different_ ports for the two channels to the client. If not try that.

> everthing is fine ('ls /root')
> <SNIP>
> 3927 select(5, [3 4], NULL, NULL, NULL) = 1 (in [3])
> 3927 read(3,
> "Desktop\nblfs.txt\nc_samples\ngew_bereitschaft.txt\ngew_route.sh\nlinux_disk.txt\nminicom.log\nplatin.ini\nsapgui.png\nsapmsg.ini\nsaproute.ini\nstaroffice7\nwebsite\n",
> 1024) = 154
> 3927 write(1,
> "Desktop\nblfs.txt\nc_samples\ngew_bereitschaft.txt\ngew_route.sh\nlinux_disk.txt\nminicom.log\nplatin.ini\nsapgui.png\nsapmsg.ini\nsaproute.ini\nstaroffice7\nwebsite\n",
> 154) = 154
> 3927 select(5, [3 4], NULL, NULL, NULL) = 1 (in [3])
> 3927 read(3, "", 1024) = 0
> 3927 close(3) = 0


Ok, the data from the successfull call of 'ls' come in via file
desciptor 3.

> 3927 close(4) = 0
> </SNIP>


> something wrong ('ls /dfdddfd' #a non existing file)
> <SNIP>
> 3934 select(5, [3 4], NULL, NULL, NULL) = 1 (in [3])
> 3934 read(3, "ls: ", 1024) = 4
> 3934 write(1, "ls: ", 4) = 4
> 3934 select(5, [3 4], NULL, NULL, NULL) = 1 (in [3])
> 3934 read(3, "", 1024) = 0
> 3934 close(3) = 0
> 3934 close(4) = 0
> </SNIP>


> The output of the second trace reads the client from fd 3 (STDOUT from
> server) instead of fd 4 (STDERR from server).


So there's a problem - probably the other side didn't stuff things into
the appropriate socket.

> The trace on the server
> for ('ls')


> 3968 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS,
> -1, 0) = 0xb7e92000
> 3968 write(1,
> "Desktop\nblfs.txt\nc_samples\ngew_bereitschaft.txt\ngew_route.sh\nlinux_disk.txt\nminicom.log\
> nplatin.ini\nsapgui.png\nsapmsg.ini\nsaproute.ini\nstaroffice7\nwebsite\n",
> 154) = 154
> 3968 close(1) = 0
> 3968 munmap(0xb7e92000, 4096) = 0
> 3968 exit_group(0) = ?


This seems to be the end of ls...

> 3966 <... read resumed> "", 1024) = 0
> 3966 write(2, "", 0) = 0
> 3966 read(4,
> "Desktop\nblfs.txt\nc_samples\ngew_bereitschaft.txt\ngew_route.sh\nlinux_disk.txt\nminicom.log\n
> platin.ini\nsapgui.png\nsapmsg.ini\nsaproute.ini\nstaroffice7\nwebsite\n",
> 1024) = 154
> 3966 write(0,
> "Desktop\nblfs.txt\nc_samples\ngew_bereitschaft.txt\ngew_route.sh\nlinux_disk.txt\nminicom.log\
> nplatin.ini\nsapgui.png\nsapmsg.ini\nsaproute.ini\nstaroffice7\nwebsite\n",
> 154) = 154
> 3966 wait4(-1, NULL, 0, NULL) = 3968


You keep me guessing: is 0 the file descriptor for one of the sockets?
Without knowing what the file descriptors stand for I can hardly gues
what might go wrong. Why not post the source code instead of heavily
snipped strace output?

> for ('ls /ddddddd')3976 write(2, "ls: ", 4 <unfinished ...>
> 3974 <... read resumed> "ls: ", 1024) = 4
> 3974 write(2, "ls: ", 4) = 4
> 3974 read(4, <unfinished ...>
> 3976 <... write resumed> ) = 4
> 3976 write(2, "/ddddddd", 8) = 8
> 3976 write(2, ": No such file or directory", 27) = 27
> 3976 write(2, "\n", 1) = 1
> 3976 exit_group(1) = ?


Looks like ls is done...

> 3974 <... read resumed> "", 1024) = 0
> 3974 write(0, "", 0) = 0


Where is the part were you write to the socket? I just see a single write
of 0 bytes to 0 (all the rest seems more like something coming from 'ls'),
not even something getting actually read from the output of ls.

Just a question: did you switch from the fork/dup2/execl to using popen()?
In that case all this wouldn't be astonishing since with popen() you can't
get stdout and stderr separately...

Regards, Jens
--
\ Jens Thoms Toerring ___ Jens.Toerring@physik.fu-berlin.de
\__________________________ http://www.toerring.de
Barry Margolin

2005-01-22, 2:48 am

In article <35cu4hF4kv73tU1@uni-berlin.de>,
Jens.Toerring@physik.fu-berlin.de wrote:

> Just a question: did you switch from the fork/dup2/execl to using popen()?
> In that case all this wouldn't be astonishing since with popen() you can't
> get stdout and stderr separately...


Actually, when you use popen() you can't get stderr at all, it only
redirects stdout. You can only get stdout if the command line includes
2>&1, which explicitly merges stderr with stdout.

--
Barry Margolin, barmar@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
Thomas Beneke

2005-01-24, 7:47 am

Jens.Toerring@physik.fu-berlin.de wrote:
> Thomas Beneke <thomas.beneke@web.de> wrote:
>
>
>
> I didn't ask about "directions", I asked if you use two sockets with
> _different_ ports for the two channels to the client. If not try that.
>
>
>
>
> Ok, the data from the successfull call of 'ls' come in via file
> desciptor 3.
>
>
>
>
>
>
>
>
> So there's a problem - probably the other side didn't stuff things into
> the appropriate socket.
>
>
>
>
>
>
> This seems to be the end of ls...
>
>
>
>
> You keep me guessing: is 0 the file descriptor for one of the sockets?
> Without knowing what the file descriptors stand for I can hardly gues
> what might go wrong. Why not post the source code instead of heavily
> snipped strace output?
>
>
>
>
> Looks like ls is done...
>
>
>
>
> Where is the part were you write to the socket? I just see a single write
> of 0 bytes to 0 (all the rest seems more like something coming from 'ls'),
> not even something getting actually read from the output of ls.
>
> Just a question: did you switch from the fork/dup2/execl to using popen()?
> In that case all this wouldn't be astonishing since with popen() you can't
> get stdout and stderr separately...
>
> Regards, Jens

Hi Jens,

I didn't switch to using popen(). Over the weekend I tried to find a
solution for my problem but I didn't find.

Regards,
Thomas
Sponsored Links






Free braindumps | Software forum | Database administration forum

Copyright 2003 - 2008 webservertalk.com