Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!mnetor!uunet!husc6!sri-unix!rutgers!ames!amdcad!rpw3 From: rpw3@amdcad.AMD.COM (Rob Warnock) Newsgroups: comp.lang.c Subject: Re: Perror complication Message-ID: <18082@amdcad.AMD.COM> Date: Wed, 26-Aug-87 08:12:47 EDT Article-I.D.: amdcad.18082 Posted: Wed Aug 26 08:12:47 1987 Date-Received: Fri, 28-Aug-87 04:32:37 EDT References: <8913@brl-adm.ARPA> <8048@mimsy.UUCP> <18005@amdcad.AMD.COM> <301@ncrcan.UUCP> Reply-To: rpw3@amdcad.UUCP (Rob Warnock) Organization: [Consultant] San Mateo, CA Lines: 93 Summary: stdio/isatty leaves errno==25 (ENOTTY) Tutorial time. All old-timers please "n" now... In article <301@ncrcan.UUCP> brian@ncrcan.UUCP () writes: +--------------- | In article <18005@amdcad.AMD.COM> rpw3@amdcad.UUCP (Rob Warnock) writes: | > ...and also avoids the "Not a typewriter" syndrome. | Sorry if this has been hashed here before. but what is this syndrome? I | remember working with a guy who was writing some code, and he kept complaining | that after his system calls, errno was being set to 25 (ENOTTY). I beleive | this is what you are talking about, but what causes it? +--------------- Chain of causes/events: 1. Unix does not clear errno on successful system calls, it only *sets* it on unsuccessful ones. (Why? Who knows/cares! It's there, and I'm sure some program somewhere would break if you changed it at this late date...) 2. Stdio packages which do "clever" line buffering of stdout on terminals (BSD 4.x and some System-V's) call "isatty()" to see if the file descriptor is a terminal. "Isatty()" does the system call "ioctl(fd, TIOCGETP, &tmp)" (Berkeley) or "ioctl(fd, TCGETA, &tmp)" (System-V), attempting to get the TTY characteristics. If the call succeeds, fine, set line buffering. If it fails (e.g., when your stdout is a pipe or a file), errno gets set to 25 (ENOTTY). 3. Many programs have a common routine [often called "panic()", "fatal()", or "die()"] they use to print error messages and exit, and some of those blindly call "perror()" (or private equivalent) to print an error message, and seeing errno set [due to #1 & #2 above], print the corresponding message from "sys_errlist[]". By the way, the usual string in "sys_errlist[25]" is "Not a typewriter". 4. The program detects an error of some kind which is *NOT* the immediate result of a unsuccessful system call, and because of #3, prints "Not a typewriter" instead of anything useful. Result? User is confused. How can #4 happen? At least three ways: a. An actually successful system call returns a value which the program mistakenly is in error. A typical example is a negative return value (including "-1") which the program wasn't expecting [such as from "nice()"]. b. The program detects an inconsistency in its input *data*, and "panic()"s. Errno is set even though no user-visible system call has failed. Per #3, "panic()" mistakenly assumes non-zero errno means use "perror()" (or equiv). This one is not quite so stupid as it looks. Many programs have an internal convention of "-1" or a negative value as an error return from lower-level routines. If an error occurs in a low-level routine, each level sees the "-1" return and backs out to the next higher. At the top level, some error handling occurs, using the value left in errno by the low-level routine that detected the error. Unfortunately, sloppy code may return "-1" as an error (due to bad input data, etc.) *without* explicitly setting errno, so the left-over "25" gets used. c. An actual system call error occurs, but while handling the error stdio is touched for the first time, and the errno value from the user's system call gets stomped on by the errno value from the failing "ioctl()" in "isatty()". Example: char *fname = "/No/such/file/exists"; if (open(fname, 0) < 0) { fprintf(stderr, "File '%s', ", fname); perror("open failed"); exit(1); } Between the failing "open()" and the "perror()", "fprintf()" opens stderr for the first time. If stderr has been redirected to a non-TTY, errno will be set to ENOTTY by the "isatty()". (While 4.3 BSD will not not call "isatty()" except on stdout, some programs are so broken as to do output to stdout between the failing system call and the "perror()"!) The solution is not fun, but starts with always clearing errno before a system call (which was what the "ERRED(x)" macro was about in the previous article). Then, *ALWAYS* make sure that you don't use errno when it's not germane. One way is to include an error-code argument in any "die()" routine; you can always say 'die(errno, "message...")' when errno's known to be valid. If an error is not due to a system call, either call "die(0,...)" or explicitly use some error value chosen to be "appropriate". For example, if the user has violated some protection boundary, say 'die(EACCESS, "That's not allowed")'. (Actually, please use a better message than that!) Finally, in any error- handling routine, save errno early so it's not lost, and *ALWAYS* print the numeric code in addition to any other messages. - Rob ("Help stamp out 'Not a typewriter'!") Warnock UUCP: {amdcad,fortune,sun,attmail}!redwood!rpw3 ATTmail: !rpw3 DDD: (415)572-2607 USPS: 627 26th Ave, San Mateo, CA 94403