Why does write() to stdin work?

I

ishwar.rattan

Tried the following code under gcc and it writes to stdin??

#include <stdio.h>
#include <stdlib.h>

int main()
{
int k;
for(k= 0; k < 4; k++)
write(k, "Hello world!\n", 13);
}

Any poiners?
-ishwar
 
O

osmium

Tried the following code under gcc and it writes to stdin??

#include <stdio.h>
#include <stdlib.h>

int main()
{
int k;
for(k= 0; k < 4; k++)
write(k, "Hello world!\n", 13);
}

Any poiners?

There is an "fwrite()" in C but no "write()". so you must have stumbled
across a gcc extension. There is probably a command line way to force gcc
to do ANSI C if you don't like this result.
 
J

James Kuyper

Tried the following code under gcc and it writes to stdin??

#include <stdio.h>
#include <stdlib.h>

int main()
{
int k;
for(k= 0; k < 4; k++)
write(k, "Hello world!\n", 13);
}

Any poiners?

write() is not a C standard library function, so the reason why it did
or did not work depends upon the library that provides write(), so your
question should be directed to a forum appropriate to that library. If,
for instance, it's the Unix standard library function, then you should
try comp.unix.programmer.

How do you know that it worked? As far as I can tell, your program makes
no attempt to determine whether or not the call to write() was
successful. For example, if this is the Unix function, your code doesn't
check the return value from write(), which should be 13 for a successful
call, nor does it check the value of errno, which should be 0 after a
successful call, unless it was already set to a non-zero value before
the call.
 
I

ishwar.rattan

write() is not a C standard library function, so the reason why it did

or did not work depends upon the library that provides write(), so your

question should be directed to a forum appropriate to that library. If,

for instance, it's the Unix standard library function, then you should

try comp.unix.programmer.



How do you know that it worked? As far as I can tell, your program makes

no attempt to determine whether or not the call to write() was

successful. For example, if this is the Unix function, your code doesn't

check the return value from write(), which should be 13 for a successful

call, nor does it check the value of errno, which should be 0 after a

successful call, unless it was already set to a non-zero value before

the call.

write() a is system call Unix systems, does not return failure and I will try comp.unix.programmer group.

Thanks for the effort.

-ishwar
 
K

Keith Thompson

osmium said:
There is an "fwrite()" in C but no "write()". so you must have stumbled
across a gcc extension. There is probably a command line way to force gcc
to do ANSI C if you don't like this result.

write() isn't a gcc extension; it's defined by POSIX.
 
N

Nobody

write() a is system call Unix systems, does not return failure

On success, it returns the number of bytes written. On failure, it returns
-1 and sets errno to indicate the nature of the failure. In your example,
it will return -1 and set errno to EBADF.
 
E

Edward A. Falk

It doesn't work.

You just got very lucky the one time you tried it, probably because you
left stdin connected to your terminal which is also an output device
*and* you happened to be linked to a runtime library that let you get
away with it. Under different conditions, your code would probably fail.

You have encountered undefined behavior. From my quotes file:

What undefined means is:

undefined.
Do not rely on the results.
You have gone outside the domain of the function
You have broken the programming model of the C language.
Here there be dragons!
The implementors can do anything they want.
Be careful.
Use a different algorithm.
This is non-portable.
Don't do it.


Here's one of the rules about being a good programmer: Don't
just try things until they work. Read the documentation and
write code that adheres to the specifications. Just because
it worked today doesn't mean it will work tomorrow.
 
G

glen herrmannsfeldt

On success, it returns the number of bytes written. On failure,
it returns -1 and sets errno to indicate the nature of the failure.
In your example, it will return -1 and set errno to EBADF.

There is no rule against writing to file descriptor zero.

The convention for inetd programs is that they read and write
of fd 0.

If fd 0 is a terminal, writing should be fine.

-- glen
 
I

Ian Collins

Tried the following code under gcc and it writes to stdin??

#include <stdio.h>
#include <stdlib.h>

int main()
{
int k;
for(k= 0; k < 4; k++)
write(k, "Hello world!\n", 13);
}

Just one thing to add: check the return value of write (always good
practice) and you will see where your code fails.
 
K

Keith Thompson

It doesn't work.

You just got very lucky the one time you tried it, probably because you
left stdin connected to your terminal which is also an output device
*and* you happened to be linked to a runtime library that let you get
away with it. Under different conditions, your code would probably fail.

You have encountered undefined behavior. From my quotes file:

What undefined means is:

undefined.
Do not rely on the results.
You have gone outside the domain of the function
You have broken the programming model of the C language.
Here there be dragons!
The implementors can do anything they want.
Be careful.
Use a different algorithm.
This is non-portable.
Don't do it.


Here's one of the rules about being a good programmer: Don't
just try things until they work. Read the documentation and
write code that adheres to the specifications. Just because
it worked today doesn't mean it will work tomorrow.

How is the behavior undefined?

If you mean that the C standard doesn't define the behavior of
write(), that's true but hardly useful; the POSIX standard does.

Somewhat off-topic:

As far as I can tell from a quick reading of the POSIX specification,
a call to write() can either succeed or fail. I'd naively expect
it to fail on an attempt to write to a file descriptor not opened
for writing, but I don't see anything that says it's *required*
to fail. And even if it were, failure is not undefined behavior;
write() would simply return -1 and set errno.

(I certainly think that writing to stdin is a silly thing to do.)
 
B

Barry Schwarz

How is the behavior undefined?

If he is indeed using the posix function, then the third argument
needs to be a size_t, not an int. But there is no prototype in scope
for write so the compiler does not know to convert the 13 to a size_t.
size_t need not have the same size as int and unsigned int..

And the second argument passes muster only because void* and char* are
guaranteed to have the same representation and alignment. I don't
think the absence of the const qualifier on this argument changes
anything one way or the other.
 
J

James Kuyper

Just one thing to add: check the return value of write (always good
practice) and you will see where your code fails.

#define _POSIX_C_SOURCE 1
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
int k;
printf("stdin:%d stdout:%d stderr:%d\n",
fileno(stdin), fileno(stdout), fileno(stderr));
for(k= 0; k < 4; k++)
{
errno = 0;
int bytes = write(k, "Hello world!\n", 13);
printf("k:%d bytes:%d errno:%d\n", k, bytes, errno);
}
return 0;
}


Output:
stdin:0 stdout:1 stderr:2
Hello world!
k:0 bytes:13 errno:0
Hello world!
k:1 bytes:13 errno:0
Hello world!
k:2 bytes:13 errno:0
k:3 bytes:-1 errno:9
 
E

Edward A. Falk

As far as I can tell from a quick reading of the POSIX specification,
a call to write() can either succeed or fail. I'd naively expect
it to fail on an attempt to write to a file descriptor not opened
for writing, but I don't see anything that says it's *required*
to fail.

That's the undefined part. OP went outside the spec and got
lucky. That won't always happen.
 
K

Keith Thompson

That's the undefined part. OP went outside the spec and got
lucky. That won't always happen.

My interpretation of the POSIX spec is that write() either succeeds
or fails. If you give it a bad pointer as the second argument,
or a size for the third argument that exceeds the available data
pointed to by the second argument, the behavior is undefined.
But if you pass 0 (standard input) as the first argument, that's
a valid file descriptor argument -- just one you might not have
*permission* to write to.

Quoting the POSIX spec
(http://pubs.opengroup.org/onlinepubs/9699919799/toc.htm):

These functions shall fail if:
...
[EBADF]
The fildes argument is not a valid file descriptor open for
writing.

That looks pretty well-defined to me.

The implementation *might* be non-conforming because it permits
writes to standard input, but I don't see any permission for the
implementation to throw up its hands and unleash the nasal demons.
I'd have to look further to determine whether 0 may be considered
to be "open for writing"; searching the spec for "0" is unlikely
to be useful.
 
K

Keith Thompson

Barry Schwarz said:
[...]
How is the behavior undefined?

If he is indeed using the posix function, then the third argument
needs to be a size_t, not an int. But there is no prototype in scope
for write so the compiler does not know to convert the 13 to a size_t.
size_t need not have the same size as int and unsigned int..

And the second argument passes muster only because void* and char* are
guaranteed to have the same representation and alignment. I don't
think the absence of the const qualifier on this argument changes
anything one way or the other.

There is no *declaration* in scope, prototype or not. The call is a
constraint violation; there is no implication in the standard that it
will even *attempt* to convert the int argument to size_t.

So yes, given the missing #include directive the program's behavior is
undefined *if* the compiler doesn't simply reject it.

(But that's not what Ed Falk was referring to.)
 
J

James Kuyper

On 04/24/2013 11:38 AM, Keith Thompson wrote:
....
My interpretation of the POSIX spec is that write() either succeeds
or fails. If you give it a bad pointer as the second argument,
or a size for the third argument that exceeds the available data
pointed to by the second argument, the behavior is undefined.
But if you pass 0 (standard input) as the first argument, that's
a valid file descriptor argument -- just one you might not have
*permission* to write to.

Quoting the POSIX spec
(http://pubs.opengroup.org/onlinepubs/9699919799/toc.htm):

These functions shall fail if:
...
[EBADF]
The fildes argument is not a valid file descriptor open for
writing.

That looks pretty well-defined to me.

The implementation *might* be non-conforming because it permits
writes to standard input, but I don't see any permission for the
implementation to throw up its hands and unleash the nasal demons.
I'd have to look further to determine whether 0 may be considered
to be "open for writing"; searching the spec for "0" is unlikely
to be useful.

I searched for stdin, and found:

"At program start-up, three streams shall be predefined and need not be
opened explicitly: standard input (for reading conventional input),
standard output (for writing conventional output), and standard error
(for writing diagnostic output)."
Note that it does not say "opened for reading only" or "opened for
writing only". Oddly enough, it does specify:

"The stderr stream is expected to be open for reading and writing." I've
never even thought about the possibility of reading from stderr - I
wonder what the use case was that prompted that change (it was
introduced in Issue 6)? If stderr is seekable, I suppose one part of a
process could read an error message produced by a different part of the
process?
 
G

glen herrmannsfeldt

(snip)
I searched for stdin, and found:
"At program start-up, three streams shall be predefined and need not be
opened explicitly: standard input (for reading conventional input),
standard output (for writing conventional output), and standard error
(for writing diagnostic output)."
Note that it does not say "opened for reading only" or "opened for
writing only". Oddly enough, it does specify:
"The stderr stream is expected to be open for reading and writing." I've
never even thought about the possibility of reading from stderr - I
wonder what the use case was that prompted that change (it was
introduced in Issue 6)? If stderr is seekable, I suppose one part of a
process could read an error message produced by a different part of the
process?

(snip)

I haven't thought about this one for a while.

Consider programs that might have both stdin and stdout redirected,
then write a question (do you really want to ...?) to stderr, and then
wait for a reply. Where do you read the reply from?

-- glen
 
E

Edward A. Falk

If he is indeed using the posix function, then the third argument
needs to be a size_t, not an int. But there is no prototype in scope
for write so the compiler does not know to convert the 13 to a size_t.
size_t need not have the same size as int and unsigned int..

Ahh, I totally missed that part.

This brings me back to what I said about following the spec
and not just trying things until it works.

The write(2) man page says to #include <unistd.h> in order to
call this function. If you don't do this, then you're doing
it wrong.

I've been programming C for a living for a very long time, and
I still read the man pages before I call a function, just to
be sure.
 
K

Ken Brody

Tried the following code under gcc and it writes to stdin??

#include <stdio.h>
#include <stdlib.h>

int main()
{
int k;
for(k= 0; k < 4; k++)
write(k, "Hello world!\n", 13);
}

Any poiners?

My guess is that, in your environment, stdin and stdout, while being
different handles, are actually duplicate read/write handles to the current
tty. At least by default.

Consider redirecting stdin, so that it is not a duplicate of stdout, and see
if you get the same results:

echo | my_program

However, this whole topic is OT for clc, as write() is not standard C but
POSIX. And even then, you may be dealing with UB, as I doubt that there is
any guarantee that stdin is writable.
 
K

Ken Brody

(snip)

I haven't thought about this one for a while.

Consider programs that might have both stdin and stdout redirected,
then write a question (do you really want to ...?) to stderr, and then
wait for a reply. Where do you read the reply from?

From "/dev/tty", of course. :)

(BTW, I wouldn't use stderr for asking the question, either.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top