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