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