Managing Failures

In most cases, [2] if a system call or library function is unsuccessful , it returns a value of -1 and assigns a value to an external (global) variable called errno to indicate what the actual error is. The defined constants for all error codes can be found in the header file (or in on some systems). By convention, the defined constants are in uppercase and start with the letter E . It is a good habit to have the invoking program examine the return value from a system call or library function to determine if it was successful. If the invocation fails, the program should take an appropriate action. A common action is to display a short error message and exit (terminate) the program. The library function perror can be used to produce an error message.

[2] This type of hedging is necessary, since system calls/library functions that return an integer value usually return a -1 on failure, while those that return a pointer return a NULL pointer. However, as these routines are written by a disjointed set of programmers with differing ideas on what should be done, a return value that does not meet this rule of thumb is occasionally encountered .

For each system call and library function discussed in detail in the text, a summary table is given. The summary table is a condensed version of manual page information. The format of a typical summary table (in this case the one for perror ) is shown in Figure 1.5.

Figure 1.5. Explanation of the summary table format.

The summary table for perror indicates the header file must be included if we are to use perror . Notice that the header file , which was mentioned previously, is not referenced in the summary table. The file is included only if the defined constants for specific error codes are to be referenced. The perror library function takes a single argument, which is a pointer to a character string constant (i.e., const char * ). In addition, the perror library function does not return a value (as indicated by the data type void ) and will not modify errno if it itself fails.

A program example using systems calls that provides some error checking by perror and errno is shown in Program 1.2.

Program 1.2 Using errno and perror .

File : p1.2.cxx /* Checking errno and using perror */ #include + #include // needed for perror #include // needed for exit #include // needed for read and write using namespace std; extern int errno; 10 int main(int argc, char *argv[ ]) { int n_char = 0, // # of chars read buffer[10]; // temporary buffer + // Initially n_char is set to 0 and errno is 0 by default cout << "n_char = " << n_char << " errno = " << errno << endl; // Display a prompt to stdout 20 n_char = write(1, "Enter a word: ", 15); // Use the read system call to obtain 10 characters from stdin + n_char = read(0, buffer, 10); cout << "n_char = " << n_char << " errno = " << errno << endl; if (n_char == -1) { // If the read has failed perror(argv[0]); 30 exit(1); } n_char = write(1, buffer, n_char); // Display the characters read return 0; + }

Notice that to use the errno variable it must first be declared as an external ( extern ) integer at the top of the program. If this program is run, the initial output indicates that both n_char and errno contain the value 0. Figure 1.6 shows the output if the user enters the word testing when prompted.

Figure 1.6 Initial run of Program 1.2 with no errors.

linux$ p1.2 n_char = 0 errno = 0 Enter a word: testing n_char = 8 errno = 0 testing

In this case the read system call did not fail and has instead, as defined in the manual page, returned the number of characters read from standard input (the keyboard). Note, as we have used read in the program, not cin , the newline will be one of the characters that is read and counted. As there was no error, the value in errno was not modified and remained at 0. Figure 1.7 shows the output if we run the program again and input more than 10 characters when prompted (in hopes of generating an error).

Figure 1.7 Second run of Program 1.2 with additional keyboard input.

$ p1.2 n_char = 0 errno = 0 Enter a word: testing further n_char = 10 errno = 0 testing fu$rther rther: Command not found.

This time the program reads exactly 10 characters and displays them. The remaining characters are left in the input buffer and end up being processed by the operating system after the program finishes execution. This produces the output of the strange line testing fu$rther followed by the line rther: Command not found . The characters testing fu are displayed by the program. The Command not found message is generated by the operating system when it attempts to execute the leftover input rther as a command. In this case, providing more input values than needed (i.e., extra characters) does not cause the read system call to fail, and as a result errno is not changed.

However, if we change the file number for the read system call to 3 (a file number that has not been opened versus [standard input] which is automatically opened for us by the operating system when the program runs), the read system call will fail. When run, the program output will be as shown in Figure 1.8.

Figure 1.8 Third run of Program 1.2 with an induced error.

linux$ p1.2 n_char = 0 errno = 0 Enter a word: n_char = -1 errno = 9 p1.2: Bad file descriptor

As expected, this time the return value from the read system call is -1. The external variable errno now contains the value 9 that is equivalent to the symbolic constant EBADF defined in the file. [3] If we call perror with a NULL argument, "", the message "Bad file descriptor" will be displayed (the error message the system associates with error code 9). As noted, perror does take one argument: a character pointer. If passed a character pointer to a valid string, perror will display the referenced string followed by a colon ( : ) and then append its predefined error message. Programmers often use the argument to perror to qualify the error message (e.g., to pass the name of the executing program, as was done in the prior example) or in the case of file manipulation, pass the name of the current file. Unfortunately, perror issues a new line following the error message it produces, thus preventing the user from appending additional information to the perror display line. There are two ways around this oversight.

[3] Again, in some Linux environments you may find that this constant is actually defined in the errno.h include file located in the directory /usr/include/asm directory.

Associated with perror are two additional external variables. These variables are extern const char *sys_errlist[ ] and extern int sys_nerr . The external variable sys_nerr contains a value that is one greater than the largest error message number value, while sys_errlist is a pointer to an external character array of error messages. In place of calling perror to return the specific error, we may (if we have provided the proper declarations) use the value in errno to index the sys_errlist[ ] array to obtain the error message directly.

Another approach to error message generation is to use the library function strerror (see Table 1.2).

Table 1.2. Summary of the strerror Library Function.

Include File(s)

Manual Section

3

Summary

char *strerror(int errnum);

Return

Success

Failure

Sets errno

Reference to error message

   

The strerror function maps the integer errnum argument (which is usually the errno value) to an error message and returns a reference to the message. The error message generated by strerror should be the same as the message generated by perror . If needed, additional text can be appended to the string returned by strerror .

Furthermore, Linux provides a command-line utility program called perror that returns the error message associated with a specific error code. A sample call of this utility follows :

linux$ perror 9 Error code 9: Bad file descriptor

Note that the system never clears the errno variable (even after a successful system call). It will always contain the value assigned by the system for the last failed call. Appendix B, " Linux Error Messages ," contains additional information on error messages.

EXERCISE

Write a program to display all of the available system error messages in a numbered two- columns -per-line format.

EXERCISE

The first argument to the read/write system call is an integer value indicating the file descriptor. When a program executes, the operating system will automatically open three file descriptors: stdin (standard input, which defaults to the keyboard and is referenced by the value 0), stdout (standard output, which defaults to the terminal [screen] and is referenced by the value 1), and stderr (standard error, which defaults to the console device and is referenced by the value 2). If the last write in Program 1.2 is written to 0 (standard inputthe keyboard), the program will still compile, run, produce output, and not generate an error message. Why is this? One place to start to unravel this mystery might be the command apropos stdin .

EXERCISE

Write your own error messaging function that is called when a file manipulation failure occurs. The function should provide a more descriptive, user-friendly interface than perror . It might be helpful to examine the header file (as noted previously, an alternate location for this file is the /usr/include/asm directory) and the manual page entry for intro in Section 2 (i.e., man 2 intro ) prior to starting this assignment.

Категории