[net.unix] '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)

jab@uokvax.UUCP (02/08/84)

#R:proper:-95700:uokvax:6100019:000:1154
uokvax!jab    Feb  5 21:26:00 1984

/***** uokvax:net.unix / proper!gam /  6:58 pm  Feb  2, 1984 */
As a maintainer and supporter of existing software, I am getting
tired of programs whose terminating action is:

	exit(1);
/* ------------- */

This person has a point, and there's a response saying "well, use 'perror'".
However, the "exit code" is something that classically was taken as a boolean,
meaning "yes, it worked" or "no, it didn't work". I won't defend that, but
let me remind you that in certain parts of the software world, those exit
codes are all important. Imagine the following exit status:

	#define	DIEDHORRIBLY	03231

where if the program exists with "DIEDHORRIBLY", something terrible has 
happened and the shell should log the user out. VAX/VMS has such a status,
and in fact, the command interpreter is responsible for telling you what
went wrong (it gets its information from, you guessed it, the exit status.)

No matter how bad it seems, there's always something worse. Probably the
most handy set of exit codes are of the form where one status means "what you
requested was stupid" and another means "well, I did what you told me and
got nothing".

	Jeff Bowles

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?)

dm%bbn-unix@sri-unix.UUCP (02/10/84)

From:  Dave Mankins <dm@bbn-unix>

What's wrong with PERROR is that it's dreadfully limited.  Since
perror only allows you to give a canned, inflexible message (plus
the error message), the amount of detail you can stick into an
error message is restricted. 

BBN has a set of routines (the cmderr family, and errmsg) which
do this very well.  Cmderr, unfortunately depends on the
non-portable "%r" (for "remote") printf format discussed in this
forum a few months ago. 

I recently moved to a new computer system (at MIT's Project
Athena) which doesn't have these, and the first thing I did was
add errmsg (which is portable), at least, and I put it into a
LIBRARY (and wrote a manual page, too, of course) so I could use
it in every program I write or modify:

/* ---------------------- e r r m s g --------------------------- */
/*
 *  Returns system error string.
 *  Argument:
 *      if error is positive, assume that it is an error number.
 *      if error is 0, then use the message associated with the
 *      current value in errno.
 */
char *
errmsg(error)
    int error;
{
    extern int errno;

    if(error == 0)
	error = errno;
    if(error <= 0 && error > sys_nerr)
	return("Unknown error");
    else
	return(sys_errlist[error]);
}

(Forgive me, I'm reconstructing this from memory, and I don't
have access to the Athena system right now--I think the
declarations of char *sys_errlist[] and int sys_nerr are
available from some include file on 4.xBSD.  If you don't have
those, look at the source for perror, and see where it gets its
error message strings (if you don't have the source for perror, I
guess you could use adb on a program which uses it, and
rebuild the table--I can't tell you, cause Mother Bell wouldn't like
it if I did).)

This routine then allows: 

    if((file_pointer = fopen(file, "r")) == NULL)
    {
	fprintf(stderr, "%s: Can't open \"%s\"; %s.\n",
	    progname, file, errmsg(0));
	...
    }

Which produces an error message:

	rabbit: Can't open "/usr/dm/lettuce";  No such file or directory.

And which is almost as easy to use as perror, except that it
ENCOURAGES your error message to contain more information.
[Sigh, it's still not as easy to use as cmderr...]

It's amazing how quickly you can find and fix problems when your
error messages contain that much information.  I'm truly tired of
programs which say "Can't open file." Can't open what file?  Why
not?  As a system maintainer I don't have time to look at the
sources of some god-awful huge program to figure out what it's
complaining about. 

Now, the problem with this scheme is that it requires the error
messages to be compiled into the program, so programs compiled a
long time ago won't know about new system error codes (I don't
believe in semantic overloading of error messages) when they
appear.  At BBN, where new kernel-originated network error codes
appear with some frequency, this is a problem.  At Project
Athena, where new kernel-originated distributed filesystem errors
will be appearing with some frequency, it will also be a problem.
A better solution (which is, in fact, how BBN's stuff does all
that) is to store the error messages in a file, and open the file
and read it to get the error message.  Of course, if the error
message in question is "Too many open files" you won't be able to
open the file to read the message, so you have to watch out for
that case, (and similar cases) but that CAN be done. 

[The possibilities of errors in your error routines reminds me of
an error message which would appear occasionally on a TENEX system
I used to work on (a "JSYS" is a system call): 

    rabbit: Can't open <dm>lettuce.txt; JSYS error in JSYS 
        error routine!  AAAIIIEEEEeeee.......! 

I never did get out of the habit of looking over at the machine
to see if the lights were still flickering when that message
appeared...]

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

rpw3@fortune.UUCP (02/14/84)

#R:utah-gr:-107700:fortune:26900025:000:1602
fortune!rpw3    Feb 13 21:01:00 1984

[ Please note that only worthy ideas are worthy of satire... -rpw3]

Why not generalize your generalized "%p" format one better?  :-)

Make ALL the formats be routines, called through PrintfFormatTable["z"-"a"+1].

Let all the "standard" routines ("%d" = decimal, etc.) have their addresses
loaded into PrintfFormatTable (which must be in read/write memory) at load
time, but allow the programmer to load his/her own fave rave routines over that.(So if I want a "%j" to call routine "frazzle", somewhere early in "main()"
say 
		extern PrintfFormatTable[], frazzle();
		...
		PrintfFormatTable["j"-"a"] := frazzle;
		...

and from then on, voila!, 'printf("...%j...",arg)' will call "frazzle"
with the argument convention exactly as shown in the preceding article.
Since one of the flags to "frazzle" will be whether it was called as
"%j" or "%J", PrintfFormatTable need only be 26 entries (saving space).

In fact, this idea may be implemented WITHOUT changing the binaries
of "stdio", since all we have to do is create the user routine "pRintf"
(big-R'ted printf) which handles PrintfFormatTable completely invisibly
to "printf", which is called as needed to print individual argument types.
Of course, "pRintf" should default most of the standard formats to calling
"printf", unless superseded in PrintfFormatTable.

The only drawback I see is changing all the printf's in all our programs
to pRintf's, but what are editors for?

Rob Warnock

UUCP:	{sri-unix,amd70,hpda,harpo,ihnp4,allegra}!fortune!rpw3
DDD:	(415)595-8444
USPS:	Fortune Systems Corp, 101 Twin Dolphins Drive, Redwood City, CA 94065

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

> (Forgive me, I'm reconstructing this from memory, and I don't
> have access to the Athena system right now--I think the
> declarations of char *sys_errlist[] and int sys_nerr are
> available from some include file on 4.xBSD.  If you don't have
> those, look at the source for perror, and see where it gets its
> error message strings (if you don't have the source for perror, I
> guess you could use adb on a program which uses it, and
> rebuild the table--I can't tell you, cause Mother Bell wouldn't like
> it if I did).)

No, the declarations aren't available in an include file under 4.xBSD
(besides, that wouldn't help people running V7 or S3/S5 if it wasn't
there), but they are available in /usr/man/man3/perror.3 or, if you
want hard copy, in the manual page PERROR(3) in the UNIX (Programmer's|User's)
Manual.  Not *every* nice feature of UNIX is totally undocumented...
"sys_errlist" and "sys_nerr" are, indeed, what you said they are.

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