By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
446,389 Members | 1,795 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 446,389 IT Pros & Developers. It's quick & easy.

using write() instead of puts() in a trivial program?

P: n/a
Greetings,

I hope my greenness isn't showing too bad by asking this, but I ran across
this trivial program today that left me flabbergasted:

#define MESSAGE "This account is currently not available.\n"

int
main(int argc, char *argv[])
{
write(STDOUT_FILENO, MESSAGE, sizeof(MESSAGE));
_exit(1);
}

This is an excerpt from the newly-rewritten nologin(8) in FreeBSD. I am
not willing to accept that the person who committed this code is a fool,
thus I have to conclude that I just don't understand why exactly the
following isn't good enough for such a trivial program:

puts("This account is currently not available.");

Perhaps write() would be appropriate in a program which did intensive
amounts of output, but the meat of this program is literally 1 line long.

I tried doing a Google search, but as you can imagine, the commonality of
the "write" keyword throws off any results that might be useful, so I was
wondering some kind, generous person here could clue me in on what's going
on here. I'd greatly appreciate it.

Charles Ulrich
Nov 14 '05 #1
Share this Question
Share on Google+
24 Replies


P: n/a
Charles Ulrich <NO***@bloodysecurenym.spamnet> scribbled the following:
Greetings, I hope my greenness isn't showing too bad by asking this, but I ran across
this trivial program today that left me flabbergasted: #define MESSAGE "This account is currently not available.\n" int
main(int argc, char *argv[])
{
write(STDOUT_FILENO, MESSAGE, sizeof(MESSAGE));
_exit(1);
} This is an excerpt from the newly-rewritten nologin(8) in FreeBSD. I am
not willing to accept that the person who committed this code is a fool,
thus I have to conclude that I just don't understand why exactly the
following isn't good enough for such a trivial program: puts("This account is currently not available."); Perhaps write() would be appropriate in a program which did intensive
amounts of output, but the meat of this program is literally 1 line long. I tried doing a Google search, but as you can imagine, the commonality of
the "write" keyword throws off any results that might be useful, so I was
wondering some kind, generous person here could clue me in on what's going
on here. I'd greatly appreciate it.


If this is supposed to be a program that, when used for a user's shell,
displays the notice and then terminates, then I have to share your
confusion. Using a simple puts() call instead of a non-standard write()
call, even followed with a non-standard _exit() call, would have been
much simpler. Perhaps you could track down the writer of this program
and ask him why he wrote it that way?

--
/-- Joona Palaste (pa*****@cc.helsinki.fi) ------------- Finland --------\
\-- http://www.helsinki.fi/~palaste --------------------- rules! --------/
"Show me a good mouser and I'll show you a cat with bad breath."
- Garfield
Nov 14 '05 #2

P: n/a
begin followup to Charles Ulrich:
int
main(int argc, char *argv[])
{
write(STDOUT_FILENO, MESSAGE, sizeof(MESSAGE));
_exit(1);
}
write(2) is a system call.
_exit(2) is a system call.
[...] I just don't understand why exactly the following isn't
good enough for such a trivial program:

puts("This account is currently not available.");


puts(3) is part of the C run time library.

By using nothing but system calls the total size of the resulting
binary can be minimized. See option -nostdlib of gcc.

--
Für Google, Tux und GPL!
Nov 14 '05 #3

P: n/a

"Joona I Palaste" <pa*****@cc.helsinki.fi> wrote in message
news:bt**********@oravannahka.helsinki.fi...
If this is supposed to be a program that, when used for a user's shell,
displays the notice and then terminates, then I have to share your
confusion. Using a simple puts() call instead of a non-standard write()
call, even followed with a non-standard _exit() call, would have been
much simpler. Perhaps you could track down the writer of this program
and ask him why he wrote it that way?


Probably links in less code? Let's try this out

test1.c
#include <stdio.h>
int main(void) { puts("hello world"); return 0; }

vs.

test2.c
#include <unistd.h>
int main(void) { write(0, "hello world\n", 12); return 0; }

vs.

test3.c
#include <stdio.h>
int main(void) { printf("hello world\n"); return 0; }

All compile and execute fine in Linux... [compiled with -O2 -s]

tombox root # ls -l test?
-rwxr-xr-x 1 root root 3020 Jan 12 15:43 test1
-rwxr-xr-x 1 root root 3040 Jan 12 15:43 test2
-rwxr-xr-x 1 root root 3020 Jan 12 15:46 test3

So it turns out puts/printf is smaller ;-)

I guess the BSD guy either had a really specific reason [e.g. part of the
kernel? not in this case...] since puts is simpler and smaller!

Personally I'd just printf since I use it for all output ;-)

Tom

Nov 14 '05 #4

P: n/a
Charles Ulrich <NO***@BLOODYsecurenym.SPAMnet> writes:
I hope my greenness isn't showing too bad by asking this, but I ran across
this trivial program today that left me flabbergasted:

#define MESSAGE "This account is currently not available.\n"

int
main(int argc, char *argv[])
{
write(STDOUT_FILENO, MESSAGE, sizeof(MESSAGE));
_exit(1);
}

This is an excerpt from the newly-rewritten nologin(8) in FreeBSD.

[...]

Presumably the FreeBSD nologin program doesn't need to be portable to
anything other than FreeBSD. Given that constraint, I suppose there's
not much advantage in using the portable puts() rather than the
relatively non-portable write().

Having said that, it's not how I would have written it. puts() or
printf() would make for more legible code.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://www.sdsc.edu/~kst>
Schroedinger does Shakespeare: "To be *and* not to be"
Nov 14 '05 #5

P: n/a
Charles Ulrich <NO***@BLOODYsecurenym.SPAMnet> wrote in
news:pa****************************@BLOODYsecureny m.SPAMnet:
Greetings,

I hope my greenness isn't showing too bad by asking this, but I ran
across this trivial program today that left me flabbergasted:

#define MESSAGE "This account is currently not available.\n"

int
main(int argc, char *argv[])
{
write(STDOUT_FILENO, MESSAGE, sizeof(MESSAGE));
_exit(1);
}

This is an excerpt from the newly-rewritten nologin(8) in FreeBSD. I
am not willing to accept that the person who committed this code is a
fool, thus I have to conclude that I just don't understand why exactly
the following isn't good enough for such a trivial program:

puts("This account is currently not available.");

Perhaps write() would be appropriate in a program which did intensive
amounts of output, but the meat of this program is literally 1 line
long.

I tried doing a Google search, but as you can imagine, the commonality
of the "write" keyword throws off any results that might be useful, so
I was wondering some kind, generous person here could clue me in on
what's going on here. I'd greatly appreciate it.

Charles Ulrich


Finding an answer to such silly problem is the same as asking and trying
to find an answer to a problem like "I have to go to the shop which is
across a street, I chose to go by bike. Maybe I should have chosen to
go to the shop by my shiny car, my shiny helicopter, my elephant?".
P.Krumins
Nov 14 '05 #6

P: n/a
Charles Ulrich wrote:

Greetings,

I hope my greenness isn't showing too bad by asking this, but I ran across
this trivial program today that left me flabbergasted:

#define MESSAGE "This account is currently not available.\n"

int
main(int argc, char *argv[])
{
write(STDOUT_FILENO, MESSAGE, sizeof(MESSAGE));
_exit(1);
}

This is an excerpt from the newly-rewritten nologin(8) in FreeBSD. I am
not willing to accept that the person who committed this code is a fool,
thus I have to conclude that I just don't understand why exactly the
following isn't good enough for such a trivial program:

puts("This account is currently not available.");


Various people have speculated about the pros and
cons and possible motivations for this code, but no one
yet seems to have mentioned the more interesting aspect:
the code outputs one more character than it (probably)
intends.

If the trailing '\0' is actually required for some
peculiar reason, that's unusual enough that a comment
in the source should be mandatory. If (as seems more
likely) the trailing '\0' is a bug, the code stands as
an object lesson in why stupid optimizations are stupid.

--
Er*********@sun.com
Nov 14 '05 #7

P: n/a
Keith Thompson wrote:
Charles Ulrich <NO***@BLOODYsecurenym.SPAMnet> writes:
I hope my greenness isn't showing too bad by asking this, but I
ran across this trivial program today that left me flabbergasted:

#define MESSAGE "This account is currently not available.\n"

int
main(int argc, char *argv[])
{
write(STDOUT_FILENO, MESSAGE, sizeof(MESSAGE));
_exit(1);
}

This is an excerpt from the newly-rewritten nologin(8) in FreeBSD.

[...]

Presumably the FreeBSD nologin program doesn't need to be portable
to anything other than FreeBSD. Given that constraint, I suppose
there's not much advantage in using the portable puts() rather than
the relatively non-portable write().

Having said that, it's not how I would have written it. puts() or
printf() would make for more legible code.


This may have to run in circumstances where the C initialization
cannot function, thus making stdout unavailable.

--
Chuck F (cb********@yahoo.com) (cb********@worldnet.att.net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net> USE worldnet address!
Nov 14 '05 #8

P: n/a
On Mon, 12 Jan 2004 23:09:24 +0000, Peteris Krumins wrote:
Finding an answer to such silly problem is the same as asking and trying
to find an answer to a problem like "I have to go to the shop which is
across a street, I chose to go by bike. Maybe I should have chosen to
go to the shop by my shiny car, my shiny helicopter, my elephant?".
P.Krumins


It's pretty easy to say that in retrospect, isn't it? But look at it from
my point of view. I'm just now emerging from being a complete novice in C
and I stumbled across some code that made little sense given its
elementary job. I am at too low a skill level to be deciding for myself
what constitutes a "silly problem" and what does not. _That's why I asked._

Thanks and gratitude to those who provided helpful input.

Charles Ulrich
Nov 14 '05 #9

P: n/a
On 12 Jan 2004 20:10:59 GMT, Alexander Bartolich
<al*****************@gmx.at> wrote:
begin followup to Charles Ulrich:
int
main(int argc, char *argv[])
{
write(STDOUT_FILENO, MESSAGE, sizeof(MESSAGE));
_exit(1);
}


write(2) is a system call.
_exit(2) is a system call.
[...] I just don't understand why exactly the following isn't
good enough for such a trivial program:

puts("This account is currently not available.");


puts(3) is part of the C run time library.

By using nothing but system calls the total size of the resulting
binary can be minimized. See option -nostdlib of gcc.


Also, when the C standard library is shared, and its file is corrupt
or inaccessible, minimum programs can run.

- Sev

Nov 14 '05 #10

P: n/a
Keith Thompson wrote:

(snip)
Presumably the FreeBSD nologin program doesn't need to be portable to
anything other than FreeBSD. Given that constraint, I suppose there's
not much advantage in using the portable puts() rather than the
relatively non-portable write(). Having said that, it's not how I would have written it. puts() or
printf() would make for more legible code.

In the days of dynamic link libraries, it is important that some
programs be able to run without dynamic linking. Now, it could
use the static library, but it will be a lot smaller not to.

-- glen

Nov 14 '05 #11

P: n/a
In <pa****************************@BLOODYsecurenym.SP AMnet> Charles Ulrich <NO***@BLOODYsecurenym.SPAMnet> writes:
I hope my greenness isn't showing too bad by asking this, but I ran across
this trivial program today that left me flabbergasted:

#define MESSAGE "This account is currently not available.\n"

int
main(int argc, char *argv[])
{
write(STDOUT_FILENO, MESSAGE, sizeof(MESSAGE));
_exit(1);
}

This is an excerpt from the newly-rewritten nologin(8) in FreeBSD. I am
not willing to accept that the person who committed this code is a fool,
thus I have to conclude that I just don't understand why exactly the
following isn't good enough for such a trivial program:

puts("This account is currently not available.");

Perhaps write() would be appropriate in a program which did intensive
amounts of output, but the meat of this program is literally 1 line long.


write() is also appropriate for small programs that are supposed to be
statically linked, while puts() is likely to draw a lot of baggage from
the stdio library. The latter also burns more CPU cycles: in such a
program it is nothing more than a fairly expensive wrapper for a write()
call. The usage of _exit() instead of exit() also indicates a concern
for minimising the number of CPU cycles used by the program, which
explains why the application was not written as a shell script:

#!/bin/sh
echo "This account is currently not available."
exit 1

So, your answer is: the author wanted a standalone application with zero
overhead in terms of disk space and cpu cycles. It's hard to tell whether
his concerns were justified or not, considering that nologin is not even
a standard Unix utility.

Dan
--
Dan Pop
DESY Zeuthen, RZ group
Email: Da*****@ifh.de
Nov 14 '05 #12

P: n/a
On 12 Jan 2004 23:09:24 GMT, Peteris Krumins
<pk****************@inbox.lv> wrote:
I tried doing a Google search, but as you can imagine, the commonality
of the "write" keyword throws off any results that might be useful, so
I was wondering some kind, generous person here could clue me in on
what's going on here. I'd greatly appreciate it.

Charles Ulrich


Finding an answer to such silly problem is the same as asking and trying
to find an answer to a problem like "I have to go to the shop which is
across a street, I chose to go by bike. Maybe I should have chosen to
go to the shop by my shiny car, my shiny helicopter, my elephant?".


Not exactly. In this case, it's "I notice that you took the bike to
the shop rather than the elephant. Was there a reason for that?"

It's a legitimate question, and can provide valuable insight - some of
the best advice a novice can get is "read other people's code." Asking
why a presumably experienced programmer chose one method over another
can be informative and educational. I even consider it topical -
discussing the pros and cons of using standard C rather than the
non-standard write function.

In this case, the best answer is "ask the programmer." Many
open-source programmers are happy to discuss their code. If the OP
wants guesses, my guess would be that the executable using "write" is
smaller with the proper linker options.

--
Al Balmer
Balmer Consulting
re************************@att.net
Nov 14 '05 #13

P: n/a
Alan Balmer wrote:
It's a legitimate question, and can provide valuable insight - some of
the best advice a novice can get is "read other people's code." Asking
why a presumably experienced programmer chose one method over another
can be informative and educational. I even consider it topical -
discussing the pros and cons of using standard C rather than the
non-standard write function.

I'm on the opposite side of that fence, I don't think reading other
people's code is good for newbies. They don't have the experience base
to filter what they are reading. They are far better off sticking with
texts, standards and their own projects until such time as their skills
develop.

Reading code, to me, doesn't really improve programming skills. It
improves the skill of reading code, which is in itself a useful and
important skill if you are going to work in the industry.


Brian Rodenborn
Nov 14 '05 #14

P: n/a
On Tue, 13 Jan 2004 17:10:47 GMT, Default User
<fi********@boeing.com.invalid> wrote:
Alan Balmer wrote:
It's a legitimate question, and can provide valuable insight - some of
the best advice a novice can get is "read other people's code." Asking
why a presumably experienced programmer chose one method over another
can be informative and educational. I even consider it topical -
discussing the pros and cons of using standard C rather than the
non-standard write function.

I'm on the opposite side of that fence, I don't think reading other
people's code is good for newbies. They don't have the experience base
to filter what they are reading.


Which is why they come here to ask ;-)
They are far better off sticking with
texts,
Texts are not necessarily a source of good programming practices. Many
of the popular ones misinform.
standards
I may be wrong, but I don't see the standard as being useful for new
programmers. Once "how" turns into "why" and "why not", the standard
is good, but that requires some experience.
and their own projects until such time as their skills
develop.
That's probably the best, *providing* they get proper feedback.
Otherwise, just developing their own projects can do more harm than
good.
Reading code, to me, doesn't really improve programming skills. It
improves the skill of reading code, which is in itself a useful and
important skill if you are going to work in the industry.
If I don't know how to do something, and I read someone else's code
which does it, it improves my programming skills.

I think reading *good* code is educational. Maybe we need an
"approved" body of reading material <g>. It may well be that random
bits of BSD source code don't qualify.

--
Al Balmer
Balmer Consulting
re************************@att.net
Nov 14 '05 #15

P: n/a
In <40***************@boeing.com.invalid> Default User <fi********@boeing.com.invalid> writes:
Reading code, to me, doesn't really improve programming skills. It
improves the skill of reading code, which is in itself a useful and
important skill if you are going to work in the industry.


Reading well written code does improve programming skills, because you
get to learn better ways of doing certain things.

Dan
--
Dan Pop
DESY Zeuthen, RZ group
Email: Da*****@ifh.de
Nov 14 '05 #16

P: n/a
Alan Balmer wrote:
On 12 Jan 2004 23:09:24 GMT, Peteris Krumins
<pk****************@inbox.lv> wrote:
Finding an answer to such silly problem is the same as asking and trying
to find an answer to a problem like "I have to go to the shop which is
across a street, I chose to go by bike. Maybe I should have chosen to
go to the shop by my shiny car, my shiny helicopter, my elephant?".


Not exactly. In this case, it's "I notice that you took the bike to
the shop rather than the elephant. Was there a reason for that?"


Don't be silly. Why would anyone take a bike to an elephant?

<g,d&r>

--
Richard Heathfield : bi****@eton.powernet.co.uk
"Usenet is a strange place." - Dennis M Ritchie, 29 July 1999.
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
K&R answers, C books, etc: http://users.powernet.co.uk/eton
Nov 14 '05 #17

P: n/a
Dan Pop wrote:

In <40***************@boeing.com.invalid> Default User <fi********@boeing.com.invalid> writes:
Reading code, to me, doesn't really improve programming skills. It
improves the skill of reading code, which is in itself a useful and
important skill if you are going to work in the industry.


Reading well written code does improve programming skills, because you
get to learn better ways of doing certain things.

But that usually requires a sufficient skill level to be able to take
advantage of the new knowledge. Newbies can't tell good code from bad,
good practices from crap, nifty new techniques from hacky, unsafe
shortcuts. It also encourages cut-n-paste cargo cultism.


Brian Rodenborn
Nov 14 '05 #18

P: n/a
Richard Heathfield wrote:
Alan Balmer wrote:
<pk****************@inbox.lv> wrote:
Finding an answer to such silly problem is the same as asking
and trying to find an answer to a problem like "I have to go
to the shop which is across a street, I chose to go by bike.
Maybe I should have chosen to go to the shop by my shiny car,
my shiny helicopter, my elephant?".


Not exactly. In this case, it's "I notice that you took the
bike to the shop rather than the elephant. Was there a reason
for that?"


Don't be silly. Why would anyone take a bike to an elephant?


So the elephant could learn to ride it, and make than 'anyone' a
pot of money in the circus.

<d & r II>

--
Chuck F (cb********@yahoo.com) (cb********@worldnet.att.net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net> USE worldnet address!
Nov 14 '05 #19

P: n/a
In <40***************@boeing.com.invalid> Default User <fi********@boeing.com.invalid> writes:
Dan Pop wrote:

In <40***************@boeing.com.invalid> Default User <fi********@boeing.com.invalid> writes:
>Reading code, to me, doesn't really improve programming skills. It ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >improves the skill of reading code, which is in itself a useful and
>important skill if you are going to work in the industry.
Reading well written code does improve programming skills, because you ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ get to learn better ways of doing certain things.


But that usually requires a sufficient skill level to be able to take
advantage of the new knowledge.


We're talking about *improving* programming skills, not about *acquiring*
them, right?
Newbies can't tell good code from bad,
good practices from crap, nifty new techniques from hacky, unsafe
shortcuts.
Newbies can ask advanced programmers for pieces of good C code, that
can be used for this purpose. Few people are learning C alone on a
desert island...
It also encourages cut-n-paste cargo cultism.


Not in my experience. The main purpose of the exercise is to get ideas
about how to code in C, rather than finding solutions to a concrete
problem. For the latter, people usually go to algorithms books with
examples in C.

Dan
--
Dan Pop
DESY Zeuthen, RZ group
Email: Da*****@ifh.de
Nov 14 '05 #20

P: n/a
Em Mon, 12 Jan 2004 21:10:59 +0000, Alexander Bartolich escreveu:
<snip>
write(2) is a system call.
_exit(2) is a system call.
By using nothing but system calls the total size of the resulting
binary can be minimized. See option -nostdlib of gcc.


That's it. /sbin/nologin isn't linked to any library, not even libc. The
result?

$ ls -lh /sbin/nologin
-r-xr-xr-x 1 root wheel 1,9K 4 Jun 2003 /sbin/nologin

FreeBSD 5.1 i386. A hand-tweaked assembly program could hardly be smaller.
Well, it could, but that's another topic.

--
Emacs is a great operating system---it lacks a good editor, though.

Nov 14 '05 #21

P: n/a
José de Paula wrote:
That's it. /sbin/nologin isn't linked to any library, not even libc.
The result?

$ ls -lh /sbin/nologin
-r-xr-xr-x 1 root wheel 1,9K 4 Jun 2003 /sbin/nologin

FreeBSD 5.1 i386. A hand-tweaked assembly program could hardly be smaller.
Well, it could, but that's another topic.


1900 bytes just to make two system calls?

It could be made much smaller :-)

e.g. http://www.muppetlabs.com/~breadbox/...ny/teensy.html

Nov 14 '05 #22

P: n/a
In <pa****************************@ig.com.br> =?iso-8859-1?q?Jos=E9_de_Paula?= <jo***********@ig.com.br> writes:
Em Mon, 12 Jan 2004 21:10:59 +0000, Alexander Bartolich escreveu:

<snip>
write(2) is a system call.
_exit(2) is a system call.

By using nothing but system calls the total size of the resulting
binary can be minimized. See option -nostdlib of gcc.


That's it. /sbin/nologin isn't linked to any library, not even libc.


Then, the linker would complain about the unresolved write and _exit
symbols. Even this program requires minimal library support, write() and
_exit() usually being wrappers to syscall().

Dan
--
Dan Pop
DESY Zeuthen, RZ group
Email: Da*****@ifh.de
Nov 14 '05 #23

P: n/a
In <bu**********@news-rocq.inria.fr> Grumble <in*****@kma.eu.org> writes:
José de Paula wrote:
That's it. /sbin/nologin isn't linked to any library, not even libc.
The result?

$ ls -lh /sbin/nologin
-r-xr-xr-x 1 root wheel 1,9K 4 Jun 2003 /sbin/nologin

FreeBSD 5.1 i386. A hand-tweaked assembly program could hardly be smaller.
Well, it could, but that's another topic.


1900 bytes just to make two system calls?

It could be made much smaller :-)


The size of the smallest C program is determined by the size of the
startup module:

fangorn:~/tmp 110> cat test.c
int main(void)
{
return 0;
}
fangorn:~/tmp 111> gcc -s test.c
fangorn:~/tmp 112> ls -l a.out
-rwxr-xr-x 1 danpop sysprog 2912 Jan 19 15:50 a.out*

If I want to get a smaller binary, that still works, I have to start
cheating:

fangorn:~/tmp 123> cat cheat.c
int main()
{
_exit(0);
}
fangorn:~/tmp 124> gcc -s -static -nostdlib cheat.c -lc
/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 08048080
fangorn:~/tmp 125> ls -l a.out
-rwxr-xr-x 1 danpop sysprog 740 Jan 19 15:56 a.out*
fangorn:~/tmp 126> ./a.out
fangorn:~/tmp 127> echo $status
0

The ld warning was caused by the missing startup module, but this
minimal program doesn't really need it. The first program above, however,
does:

fangorn:~/tmp 128> gcc -s -static -nostdlib test.c -lc
/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 08048080
fangorn:~/tmp 129> ./a.out
Segmentation fault
fangorn:~/tmp 130> ls -l a.out
-rwxr-xr-x 1 danpop sysprog 588 Jan 19 16:00 a.out*

The missing syscall support from the statically linked executable
made it both shorter and non-functional.

Dan
--
Dan Pop
DESY Zeuthen, RZ group
Email: Da*****@ifh.de
Nov 14 '05 #24

P: n/a
Dan Pop wrote:
The size of the smallest C program is determined by the size of the
startup module:


And the size of the smallest ELF binary is determined by the size of
the ELF header ;-)

(Jose mentioned hand-tweaked assembly under FreeBSD.)

Nov 14 '05 #25

This discussion thread is closed

Replies have been disabled for this discussion.