For Perror

download For Perror

of 13

Transcript of For Perror

  • 8/3/2019 For Perror

    1/13

    Error reporting in C programs

    C is the most commonly used programming language onUNIX platforms. Despite the popularity of other languages onUNIX (such as Java, C++, Python, or Perl), all of theapplication programming interfaces (APIs) of systems havebeen created for C. The standard C library, part of every Ccompiler suite, is the foundation upon which UNIXstandards, such as Portable Operating System Interface(POSIX) and the Single UNIX Specification, were created.

    When C and UNIX were developed in the early 1970s, theconcept of exceptions, which interrupt the flow of anapplication when some condition occurs, was fairly new ornon-existent. The libraries had to use other conventions forreporting errors.

    While you're pouring over the C library, or almost any otherUNIX library, you'll discover two common ways of reporting

    failures:

    * The function returns an error or success code; if it's anerror code, the code itself can be used to figure out whatwent wrong.

    * The function returns a specific value (or range of values)to indicate an error, and the global variable errno is set toindicate the cause of the problem.

    The errno global variable (or, more accurately, symbol, sinceon systems with a thread-safe C library, errno is actually afunction or macro that ensures each thread has its ownerrno) is defined in the system header, along withall of its possible values defined as standard constants.

  • 8/3/2019 For Perror

    2/13

    Many of the functions in the first category actually return oneof the standard errno codes, but it's impossible to tell how afunction behaves and what it returns without checking the

    Returns section of the manual page. If you're lucky, thefunction's man page lists all of its possible return values andwhat they mean in the context of this particular function.Third party libraries often have a single convention that'sfollowed by all of the functions in the library but, again, you'llhave to check the library's documentation before making anyassumptions.

    Let's take a quick look at some code demonstrating errnoand a couple of functions that you can use to transform thaterror code into something more human-readable.

    Back to top

    Reporting failure

    In Listing 1, you'll find a short program that tries to open a filethat is unlikely to exist and reports the error to whomever isrunning the program, using two different techniques.

    Listing 1. The errno variable records your failures

    // errno for fun and profit

    #include

    #include #include #include #include

    const char *FILE_NAME =

  • 8/3/2019 For Perror

    3/13

    "/tmp/this_file_does_not_exist.yarly";

    int main( int argc, char **argv ){

    int fd = 0;

    printf( "Opening %s...\n", FILE_NAME );fd = open( FILE_NAME, O_RDONLY, 0644 );if( fd < 0 ) {

    // Error, as expected.perror( "Error opening file" );printf( "Error opening file: %s\n", strerror( errno ) );

    }

    return EXIT_SUCCESS;}

    When you run this program, you'll see something like Listing2.

    Listing 2. The output from Listing 1

    chrish@dhcp2 [507]$ ./Debug/errnoDemoOpening /tmp/this_file_does_not_exist.yarly...Error opening file: No such file or directoryError opening file: No such file or directory

    As you can see from the output (Listing 2), the perror()function displays the string you pass to it, followed by acolon, a space, and then the textual representation of thecurrent errno value. You can simulate this yourself by usinga printf() call and the strerror() function, which returns apointer to the textual representation of the current errno

  • 8/3/2019 For Perror

    4/13

    value.

    One detail you can't see from the output is that perror()writes its message to the standard error channel (stderr); the

    printf() call in Listing 1 is writing to the standard outputchannel (stdout).

    The strerror() function isn't necessarily thread-safe; forunknown values, it formats an error message in a staticbuffer and returns a pointer to that buffer. Additional calls tostrerror() will overwrite the contents of that buffer.

    The POSIX 1003.1 standard defines strerror_r(), whichaccepts a pointer to a buffer and a buffer size in addition tothe error value. Listing 3 shows you how to use this thread-safe version.

    Listing 3. The thread-safe strerror_r() function in action

    // Thread-safe usage of strerror_r().

    void thread_safe( int err ){char buff[256];

    if( strerror_r( err, buff, 256 ) == 0 ) {

    printf( "Error: %s\n", buff );}

    }

    The perror() and strerror()/strerror_r() functions are probablythe most commonly used error reporting methods whendealing with standard errno values. Let's take a look at someadditional error-related global variables and the standarddefined by POSIX-1003.1 errno values.

  • 8/3/2019 For Perror

    5/13

    Back to top

    Error global variables and standard values

    So, the global errno variable is set by standard C libraryfunctions (and possibly others; read the fine manual to findout if a function you intend to use sets errno) to indicatesome kind of error, be it some bad values passed in asarguments, or a failure while the function was performing itsduties.

    The perror() and strerror() functions that pull standard errordescriptions come from the global variable, sys_errlist.

    The standard C library defines two additional error-relatedglobal variables, sys_nerr (an int) and sys_errlist (an array ofpointers to char). The first is the number of standard errormessages stored in sys_errlist. Historical applications (thatis, horribly outdated legacy code) sometimes refer to these

    directly, but produce errors during compilation becausethey're declared inconsistently.

    The POSIX standard defines quite a few possible values forerrno; not all of these are applicable to every function,obviously, but they do provide developers with a large menuto choose from when writing their own functions.

    Here's an Eclipse tip: Select errno in your code, then press

    the F3 key (or right-click on errno, then choose OpenDeclaration from the context menu). Eclipse opens theerrno.h system header and highlights the declaration fromerrno, as shown in Figure 1.

    Figure 1. Showing the declaration of errno

  • 8/3/2019 For Perror

    6/13

    showing errno's declaration

    In addition to noticing that my tab settings don't match thoseof whoever wrote this file, you'll see several of the standarderror values, their symbolic names, and a brief commentdescribing each. Most system headers contain at least thismuch information for the standard errno values, so don't be

    afraid to take a look. Your system headers and manualpages are also your only source of information about thenon-standard values that your system might support.

    The standard errno values include:

    * E2BIG -- The argument list passed to the function wastoo long.

    * EACCESS -- Access denied! The user running theprogram doesn't have permission to access a file, directory,and so forth.

    * EAGAIN -- The required resource is temporarilyunavailable; if you try the operation again later, it mightsucceed.

  • 8/3/2019 For Perror

    7/13

    * EBADF -- A function tried to use a bad file descriptor (itdoesn't refer to an open file, for example, or it was used inan attempt to write to a file that was opened read-only).

    * EBUSY -- The requested resource is unavailable. For

    example, attempting to remove a directory while anotherapplication is reading it. Note the ambiguity between EBUSYand EAGAIN; obviously you'd be able to remove thedirectory later, when the reading program has finished.

    * ECHILD -- The wait() or waitpid() function tried to wait fora child process to exit, but all children have already exited.

    * EDEADLK -- A resource deadlock would occur if therequest continued. Note that this is not the sort of deadlock

    you get in multithreaded code -- errno and its friendsdefinitely can't help you track those down.

    * EDOM -- The input argument is outside of the domain ofa mathematical function.

    * EEXIST -- The file already exists, and that's a problem.For example, if you call mkdir() with a path that names anexisting file or directory.

    * EFAULT -- One of the function arguments refers to an

    invalid address. Most implementations can't detect this (yourprogram receives a SIGSEGFAULT signal and exit instead).* EFBIG -- The request would cause a file to expand past

    the implementation-defined maximum file size. This isgenerally around 2GB, but most modern file systems supportmuch larger files, sometimes requiring 64-bit versions of theread()/write() and lseek() functions.

    * EINTR -- The function was interrupted by a signal, whichwas caught by a signal handler in the program, and the

    signal handler returned normally.* EINVAL -- You passed an invalid argument to the

    function.* EIO -- An I/O error occurred; this is usually generated in

    response to hardware problems.* EISDIR -- You called a function that requires a file

  • 8/3/2019 For Perror

    8/13

    argument with a directory argument.* ENFILE -- Too many files are already open in this

    process. Each process has OPEN_MAX file descriptors, andyou're trying to open (OPEN_MAX + 1) files. Remember that

    file descriptors include things like sockets.* ENLINK -- The function call would cause a file to have

    more than LINK_MAX links.* ENAMETOOLONG -- You've created a path name

    longer than PATH_MAX, or you've created a file or directoryname longer than NAME_MAX.

    * ENFILE -- The system has too many simultaneouslyopen files. This should be a temporary condition, and it is

    unlikely to happen on a modern system.* ENODEV -- No such device or you're attempting to do

    something inappropriate for the specified device (don't tryreading from an ancient line printer, for example).

    * ENOENT -- No such file was found or the specified pathname doesn't exist.

    * ENOEXEC -- You tried to run a file that isn't executable.* ENOLCK -- No locks are available; you've reached a

    system-wide limit on file or record locks.* ENOMEM -- The system is out of memory. Traditionally,applications (and the OS itself) don't handle this gracefully,which is why you need to have more RAM than you expectto use, especially on systems that can't dynamically increasethe size of the on-disk swap space.

    * ENOSPC -- No space left on the device. You've tried towrite to or create a file on a device that's full. Again, it'straditional for applications and the OS to not handle this

    gracefully.* ENOSYS -- The system doesn't support that function.

    For example, if you call setpgid() on a system without jobcontrol, you'll get an ENOSYS error.

    * ENOTDIR -- The specified path name needs to be adirectory, but it isn't. This is the opposite of the EISDIR error.

  • 8/3/2019 For Perror

    9/13

    * ENOTEMPTY -- The specified directory isn't empty, butit needs to be. Note that an empty directory still containsthe . and .. entries.

    * ENOTTY -- You've attempted an I/O control operation on

    a file or special file that doesn't support that operation. Don'ttry setting the baud rate on a directory, for example.

    * ENXIO -- You've attempted an I/O request on a specialfile for a device that doesn't exist.

    * EPERM -- The operation isn't permitted; you don't havepermission to access the specified resource.

    * EPIPE -- You've attempted to read from or write to apipe that doesn't exist any more. One of the programs in the

    pipe chain has closed its part of the stream (by exiting, forexample).

    * ERANGE -- You've called a function, and the returnvalue is too large to be represented by the return type. Forexample, if a function returns an unsigned char value butcalculated a result of 256 or more (or -1 or less), errno wouldbe set to ERANGE and the function would return someirrelevant value. In cases like this, it's important to check

    your input data for sanity, or check errno after every call.* EROFS -- You attempted to modify a file or directorystored on a read-only file system (or a file system that wasmounted in read-only mode).

    * ESPIPE -- You attempted to seek on a pipe or First In,First Out (FIFO).

    * ESRCH -- You've specified an invalid process ID orprocess group.

    * EXDEV -- You've attempted an operation that would

    move a link across devices. For example, UNIX filesystemsdon't let you move a file between file systems (instead, youhave to copy the file, then delete the original).

    One annoying feature of the POSIX 1003.1 specification isthe lack of a no error value. When errno is set to 0, you've

  • 8/3/2019 For Perror

    10/13

    encountered no problems, except you can't refer to this witha standard symbolic constant. I've programmed on platformsthat had E_OK, EOK, and ENOERROR in their errno.h, andI've seen loads of code that includes something like Listing

    4. It would've been nice to have this covered in thespecification in order to avoid doing this sort of thing.

    Listing 4. The no error error value

    #if !defined( EOK )# define EOK 0 /* no error */#endif

    Using the sys_nerr global variable and the strerror() function,you can easily whip up some code (see Listing 5) to print outall of the built-in error messages of the system. Remember,this dumps all of the additional implementation-defined (thatis, non-standard) errno values supported by the systemyou're using. Only the errors listed above are required to

    exist on a POSIX 1003.1-conforming system, anything elseis gravy.

    Listing 5. Showing off all of your errors

    // Print out all known errors on the system.void print_errs( void ){

    int idx = 0;

    for( idx = 0; idx < sys_nerr; idx++ ) {

    printf( "Error #%3d: %s\n", idx, strerror( idx ) );}

    }

  • 8/3/2019 For Perror

    11/13

    I won't bore you with a complete list of all the errno valuessupported by my system (Mac OS X 10.4.7 at the time of thiswriting), but here's a sample of the output from the

    print_errs() function (see Listing 6).

    Listing 6. There sure are a lot of possible standard errorvalues

    Error # 0: Unknown error: 0Error # 1: Operation not permittedError # 2: No such file or directory

    Error # 3: No such processError # 4: Interrupted system callError # 5: Input/output errorError # 6: Device not configuredError # 7: Argument list too longError # 8: Exec format errorError # 9: Bad file descriptorError # 10: No child processes

    Error # 93: Attribute not foundError # 94: Bad messageError # 95: EMULTIHOP (Reserved)Error # 96: No message available on STREAMError # 97: ENOLINK (Reserved)Error # 98: No STREAM resourcesError # 99: Not a STREAMError #100: Protocol error

    Error #101: STREAM ioctl timeoutError #102: Operation not supported on socket

    That's quite a lot of errors! Luckily, most functions will onlyhave a few possible errors to report, so it's usually not that

  • 8/3/2019 For Perror

    12/13

    hard to handle them appropriately.

    Back to top

    Dealing with errors

    Adding error-handling code to your program can beannoying, tedious, and time-consuming. It can clutter up theelegance of your code, and you can get bogged downadding handlers for every conceivable error. Developersoften hate doing it.

    But, you're not doing it for yourself, you're doing it for thepeople who are going to actually use your program. Ifsomething can fail, they need to know why it failed and,more importantly, what they can do to fix the problem.

    That last part is often the bit that developers often miss.Telling the user File not found isn't nearly as helpful as tellingthem Unable to find the SuperWidget configuration file, and

    then giving them the option to select the missing file (givethem a file selection widget or something), search for themissing file (have the program look in likely places for thefile), or create a new version of the file filled with the defaultdata.

    Yes, I know this interrupts the flow of your code, but slickerror-handling and recovery really makes your application ahit with the users. And, because other developers are often

    lacking when it comes to error-handling, it's easy to do betterthan everyone else.

    Back to top

    Summary

  • 8/3/2019 For Perror

    13/13

    On UNIX, the standard error reporting mechanisms arepretty minimalistic, but that's no reason for your applicationto handle run time errors by crashing or exiting without telling

    the user what's going on.

    The standard C library and POSIX 1003.1 define a numberof possible standard error values, and a couple of handyfunctions for reporting errors and translating the errors intosomething humans can read. But these aren't really enough,developers should try harder to tell the user what's going onand give them ways of fixing or working around the problem.