[net.lang.c] 'exit

ka@hou3c.UUCP (Kenneth Almquist) (02/02/84)

The example given, in which a program exits in the case of an error without
printing an error message, is probably an unforgivable action on the part
of the programmer.  (The exception would be if the program is not intended
to be run by users, such as a mail delivery program.)  However, I don't
think that using errno as the exit status is a particularly good convention
because knowing the value is errno is often not much use if you don't know
which system call failed, and more importantly, why the program was trying
to execute the system call in the first place.  Furthermore, the convention
does not deal with errors that are not due to system calls failing.

In my experience a simple succeeded/failed error indication, combined with
a more informative printed error message, is adequate for almost all pro-
grams.  However, if a more detailed error indication is needed, a list of
error codes which relate to the function of the program should be constructed.
The "zero = successs" convention is universal enough that all programs should
follow it.  Beyond that, however, there is no widely accepted convention,
so the error codes might as well be chosen to describe the possible errors
clearly.
					Kenneth Almquist

crl@pur-phy.UUCP (Charles LaBrec) (02/03/84)

While exit(errno) is more useful than exit(1) everywhere, I would rather
see a perror() followed by an exit with a value from <sysexits.h>.  The
trouble is that not all errors come from system calls, and who really
wants to check the exit status to determine the actual error.  Furthermore,
if I do an exit(EPERM), I'd really like to know which file I failed on.

Charles LaBrec
UUCP:		pur-ee!Physics:crl, purdue!Physics:crl
INTERNET:	crl @ pur-phy.UUCP

gam@proper.UUCP (Gordon Moffett) (02/03/84)

Indeed, the example given (with no error message printed out) was
contrived and unforgivable practice.

However, why shouldn't errno be used as an exit status indicator?
At least within Unix-variants, those values are quite specific, and
readily looked up in the manuals.

And, as I implied in my original article, why not more use of perror(3)?
I find error messages such as:

	"can't open file"

are frustratingly trite (and all too common), when one could have easily
used perror(3) to produce more meaningful messages as:

	"no such file or directory"
or	"permission denied"
or	"too many open files"

which would help speed up tracking down the error.

(and of course, including the name of the file in the error message
 would be nice).

By this article, I am seeking to make more programmers aware of the
importance of good error messages, so as to make all our work easier.
-- 
Gordon A. Moffett
	{ allegra, decvax!decwrl } !amd70!proper
	hplabs!intelca!proper!gam

guy@rlgvax.UUCP (Guy Harris) (02/06/84)

> However, why shouldn't errno be used as an exit status indicator?
> At least within Unix-variants, those values are quite specific, and
> readily looked up in the manuals.

Because, to put it bluntly, there is *not* a one-to-one correspondence
between UNIX system call errors and command errors.  What's the exit
status for a syntax error in the command line?  There's no ECMDSYNTAX
in <errno.h>...

> And, as I implied in my original article, why not more use of perror(3)?
> I find error messages such as:

> 	"can't open file"

> are frustratingly trite (and all too common), when one could have easily
> used perror(3) to produce more meaningful messages as:

> ...(and of course, including the name of the file in the error message
>  would be nice).

I agree that "can't open file" isn't informative enough (UNIX isn't the
only sinner here, though; RSX-11M error messages also just say "sorry,
couldn't do it"), but one reason may be that it's a pain in the *ss to
get "perror" to say:

	frob: /etc/mumble: No such file or directory

Neither perror("frob") nor perror(filename) do what you really want (and
for the "link" system call, you usually want to print *both* names, which
"perror" won't let you).  The answer is to use "sys_errlist" and use
"fprintf" (not "printf", please, don't send error messages to stdout!) to
print the message.

	Guy Harris
	{seismo,ihnp4,allegra}!rlgvax!guy

guy@rlgvax.UUCP (Guy Harris) (02/06/84)

Unfortunately, <sysexits.h> only comes with Berkeley systems, although anybody
can offer it with their UNIX (AT&T picked up "ex"/"vi", under much pressure;
maybe they could pick this up too?).  It is the correct solution to the
problem.  Note that in VMS low-level error returns (at the same level as
"errno" codes in UNIX) are translated to higher-level error indications as
they pass up the call tree; using the <sysexits.h> codes is the equivalent.
(Just as long as UNIX programs don't print the full interpretation of the
error codes at each level as some VMS programs do!)

	Guy Harris
	{seismo,ihnp4,allegra}!rlgvax!guy

chris@umcp-cs.UUCP (02/06/84)

Speaking of "perror"... one thing I'd like to see is a ``%'' format
for printf that interprets an error number, so that I could say
(for example)

#include <stdio.h>

/*
 * Log an error message, and print it to stderr too.  'err' is a Unix(tm)
 * system error number (one of the beasts from <errno.h>).
 */
LogError (err) int err; {
    static FILE *logfile;

    /* Replace '#' with appropriate char.  Can't use 'E', sigh. */
    fprintf (stderr, "%#\n", err);
    if (logfile || (logfile = fopen ("/foo/bar/errors", "a")))
	fprintf (logfile, "%#\n", err);
}

Many times I've wanted to print the error message associated with
"errno" -- but not the way perror(3) prints it.  I've had to fall
back on "extern char *sys_errlist[]; extern int sys_nerr;" hacks.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci
UUCP:	{seismo,allegra,brl-bmd}!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris.umcp-cs@CSNet-Relay

mark@cbosgd.UUCP (Mark Horton) (02/06/84)

Of course, perror is not enough.  One of my favorite error messages
happens when a new user sits down to edit a file, and types the name
of the file.  The shell comes back with
	file: permission denied
which makes them think they don't have access to the file.  What
really happened is that they forgot to type "vi".

gam@proper.UUCP (Gordon Moffett) (02/07/84)

As a maintainer and supporter of existing software, I am getting
tired of programs whose terminating action is:

	exit(1);

A typical application is:

	p = malloc(n);
	if (p == NULL) {
		exit(1);
	}

Bad enough that the original programmer didn't code for an error message,
but furthermore was content to just provide a `non-zero' error code.

In intro(2) of volume 1 of the Unix manual set are described the various
error codes returned by system calls and subroutines.  There is an
external variable (int) called `errno' where these error codes are
stored when applicable.  So, programmers, please use:

	exit(errno);

... whenever possible, so I don't have to recode it for you.

It is useful to reference the exit status of a command and have it
provide more meaningful information that just a non-zero value.

Thank you.

SEE ALSO
	perror(3)
	"lint vs. strong typing - programmers' responsibitly", Usenet:
		net.lang
-- 
Gordon A. Moffett
	{ allegra, decvax!decwrl } !amd70!proper
	hplabs!intelca!proper!gam

guy@rlgvax.UUCP (Guy Harris) (02/07/84)

Mark's mentioning that naive users sometimes try to edit a file by typing its
name is interesting, considering that that sort of "syntax" is exactly what
"desktop shells" like the Xerox Star's and the Apple MacIntosh and Lisa's
"top-level shell" provide; to edit a file, just point to it, select it, and
open it.  (After someone ranked on the MacIntosh in net.micro, I went
downstairs and played with our Lisa a bit.  It turns out that after you've
gotten the hang of it after quite a short while, it's easy *and fast* to
perform certain manipulations - selecting and opening, closing, deleting,
etc. - and you are a lot less likely to make "typographical errors" since
you aren't typing anything, which also helps speed things up.)

	Guy Harris
	{seismo,ihnp4,allegra}!rlgvax!guy

decot@cwruecmp.UUCP (Dave Decot) (02/08/84)

Perror() is not good because it discourages specific messages.  The shells
should say:

    % file
    file: data; not executable
or
    file: program not executable by you

instead of

    % file
    file: permission denied

Also, why do we get:

    % cd /user/bin
    /user/bin: No such file or directory

when the argument to cd is never supposed to be a file?  Somebody used
the convenient perror() instead of writing a specific message.

Dave Decot		 "Non-Americans are people, too."
decvax!cwruecmp!decot    (Decot.Case@rand-relay)

mark@umcp-cs.UUCP (02/09/84)

It only takes one extra line to say:
	sprintf(s,"%s: %s",frob,mumble);  /* or whatever else you want */
	perror(s);-- 
Mark Weiser 		
UUCP:	{seismo,allegra,brl-bmd}!umcp-cs!mark
CSNet:	mark@umcp-cs 	ARPA:	mark@maryland

jonab@sdcrdcf.UUCP (Jonathan Biggar) (02/09/84)

In article <957@cwruecmp.UUCP> decot@cwruecmp.UUCP (Dave Decot) writes:
>Perror() is not good because it discourages specific messages.  The shells
>should say:
>
>    % file
>    file: data; not executable
>or
>    file: program not executable by you
>
>instead of
>
>    % file
>    file: permission denied
>

There are a couple reasons for this behavior:

1)  All the shell does is to do an exec(2) (or variant thereof) on
    the file.  It lets the kernel decide whether the user is allowed
    to execute that file.  The kernal only returns the error EACCES
    which the shell cannot distinguish between the conditions of
    having the execute bit turned off on the file or one of the
    directories it searched to get to that file.  For security
    reasons, it is best for the kernel not to give any more explicit
    information to the user.

2)  In light of the first reason, one may ask why shouldn't the shell
    do what checking it is theoretically able to do?  It shouldn't
    because that would require adding massive amounts of code to the
    shell to detect each possible error condition and report them.
    Also, the shell may not be able to determine exactly what the
    problem is because it does not have the appropriate permissions.
    (How is the shell supposed to tell the difference between a program
    that a user does not have permission to execute and data that a
    user does not have permission to execute?)

idallen@watmath.UUCP (02/11/84)

I'd rather that the shell went through the trouble of checking for the
exact cause of an error, instead of forcing me to do it.  The code to do
this checking need not even be in the shell -- when an error occurs, the
shell can call some other code to track down the error to something more
specific than "Permission denied".  If you're really clever, you might
make the further search and elaboration a user-settable option...
-- 
        -IAN!  (Ian! D. Allen)      University of Waterloo

ian@utcsstat.UUCP (Ian F. Darwin) (02/12/84)

	I agree that "can't open file" isn't informative enough (UNIX isn't the
	only sinner here, though; RSX-11M error messages also just say "sorry,
	couldn't do it"), but one reason may be that it's a pain in the *ss to
	get "perror" to say:
	
		frob: /etc/mumble: No such file or directory
	
	Neither perror("frob") nor perror(filename) do what you really want (and
	for the "link" system call, you usually want to print *both* names, which
	"perror" won't let you).  The answer is to use "sys_errlist" and use
	"fprintf" (not "printf", please, don't send error messages to stdout!) to
	print the message.
	
		Guy Harris
		{seismo,ihnp4,allegra}!rlgvax!guy

It's actually very easy to get the effect of perror(3) with a real message.
A good encapsulation of this technique appears in the Kernighan and Pike
book ``The UNIX Programming Environment''. The function
	error(s1, s2);
prints out the program name (the first line in `main' has to be
	progname = argv[0];
for this to work), then s1 and s2 (which should be either a printf
format and arg, or a simple string and null ptr), then iff there is a reasonable
value of errno and sys_errlist[errno] this too is printed. So you get:
	foo: can't read bar (No such file or directory)
just by saying
	error("can't read %s", barfile);

It would be nice if people would adopt this as a kind of `standard';
since it will be quite widely known as a result of the book, I would
say that it's probably reasonable to distribute code which calls this
function (and others in the book, such as efopen() which encapulates
the common and tedious
	if ((x=fopen(bar, "r")) == NULL) {
		... print a message ...
		exit(1);
	}
into a single call,
	x=efopen(bar, "r);

There is a lot of other good code in the book as well, but these
encapsulations are SOOOO useful that I recommend we all use them.


Ian F. Darwin, Toronto, Canada     decvax!utcsstat!darwin!ian