3
  1. Do we need to reset errno to zero before calling a function? See below code. Now the scenario is a_dest_path is an existing directory. But when I execute the code, it always tries to mkdir but returns error says that the directory can't be created because it exists. In GDB, I check errno before calling opendir() and errno is 2. And it seems errno is not set to zero during calling opendir(). So do I need to reset errno to zero before calling opendir()?

  2. errno may be changed in system() calls, then in my else if branch I check the result from system() but not opendir(). So after opendir(), do I need to assign errno to a variable then check this variable in the if..elseif..else branch?

DIR *dp = opendir(a_dest_path.c_str());
if (errno == ENOENT) {
    string mkdir_comman = "mkdir " + a_dest_path;
    system(mkdir_command.c_str());
} else if (errno == ENOTDIR) {
    printf("Destination %s exists but is not directory\n", a_dest_path.c_str());
    return k_error_not_directory;
} else if (errno == 0) {
    closedir(dp);
}
Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
JackChen255
  • 355
  • 2
  • 4
  • 12
  • For 2. I think you misunderstand how `if() {} else if () {}` works. The `else if` condition will not get tested if the first `if` condition is true – Jonathan Wakely Aug 14 '14 at 19:18
  • Clearing `errno` is only required when the function you called doesn't signify an existence of an error with its return value (e.g., a function whose return type is `void` and a function whose return value's domain is entirely meaningful for normal and not-error value). – rosshjb Apr 27 '21 at 09:44

3 Answers3

8

No, there is no need to reset errno before calling the function, because the contract reads:

The opendir() and fdopendir() functions return a pointer to the directory stream. On error, NULL is returned, and errno is set appropriately.

Test the return-value, and only look at errno when you actually have an error!

(realloc is a rare example of a function where under specific circumstances an error cannot be distinguished from success without looking at errno, though it will clear it if that is necessary for disambiguation.)

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
  • Thanks. So the errno is not set to zero if no error happens during a function call, am I right? And is it not safe to check errno in the if.else branch, because the errno may be set to another value if error happens inside the first if branch, e.g. system() function in my code? Thanks. – JackChen255 Aug 14 '14 at 18:54
  • 5
    With a few specific exceptions where documented, `errno` is only updated when an error occurs. – Remy Lebeau Aug 14 '14 at 18:56
  • 3
    Your transcription does not say anywhere that errno will be reset when those functions begin their work. So, what security do you have that errno has not been set on a previous instruction? – sergiol Oct 04 '16 at 19:16
  • 1
    @sergiol As I said, test whether an error occurred first. – Deduplicator Oct 04 '16 at 19:24
  • So errno does not tell you THAT and error occurred, it only tells you WHAT error occurred last. It would have been much more efficient to have the "something happened" combined with "what happened" where errno did both. As it sits, errno is misleading as it hangs on to the error that happened last and who knows where that happened UNLESS you reset it after every call that could have set it to a different value when an error occurred. – Cerniuk Dec 21 '19 at 03:04
  • @WilliamCerniuk At the system call level (at least in Linux) this is exactly what happens. `errno` is usually the return value of the system call, and so instead of checking for a `-1` return code and a separate `errno` value, the return code is `-errno` in the event of an error and `0` or some useful return value in the event of a success. – mtraceur Jul 17 '20 at 23:08
  • @mtraceur probably my unfamiliarity with it but if I have 12 threads running across 6 processor cores in parallel, errno does not seem to be useful unless it has separate context in each thread (??) which stands to compound the issue presented by JackChen255. Right now the answer presented by Deduplicator does not seem to fit what I see going on in my code. – Cerniuk Jul 18 '20 at 11:44
  • @WilliamCerniuk Long, long ago, when threads were added, `errno` was made per-thread. That it often gets set by libc after failed syscalls is an implementation-detail. – Deduplicator Jul 18 '20 at 11:47
  • @WilliamCerniuk Yes, `errno` is per-thread. It is actually defined as such in every modern standard and system. It looks like a global in languages like C because `errno` basically predates C having threads, and backwards compatibility locked it in. Though the way that's implemented in a language like C nowadays is by having `errno` be a macro that expands to a dereferenced result of a function that gets a per-thread pointer (like `#define errno *_errno()`), or defining `errno` to have a per-thread storage "duration" class (like `_Thread_local` or equivalent compiler extension). – mtraceur Jul 20 '20 at 01:59
  • @mtraceur, & Deduplicator, thanks both! I did not realize that `errno` is designed to informational, not actionable. I've never used linux for firmware before so thanks for the indulgence. My pattern in firmware is Make A Call -> Fork If Error Condition -> Mitigate Error Condition If Possible -> Resume. I don't check all the data values from functions to determine if an error has occurred as returned data values can very easily can be scrambled garbage if an error condition in hardware occurs (checksum error in RAM, etc). Time to jam a square peg into a round hole. :-) – Cerniuk Aug 08 '20 at 14:04
4

errno is only meaningful after a real error occurs. You have to check for the error condition first, THEN look at errno, eg:

DIR *dp = opendir(a_dest_path.c_str());
if (dp) {
    closedir(dp);
} else {
    if (errno == ENOENT) {
        string mkdir_comman = "mkdir " + a_dest_path;
        system(mkdir_command.c_str());
    } else if (errno == ENOTDIR) {
        printf("Destination %s exists but is not directory\n", a_dest_path.c_str());
        return k_error_not_directory;
    } else {
        // some other error...
    }
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 2
    This is not safe too, because errno may be changed inside the if..else if structure. What about if error happens in system(mkdir_command.c_str()), then errno is changed to the error code of system() instead of opendir() – JackChen255 Aug 14 '14 at 18:58
  • 2
    So what if `errno` changes? You are supposed to examine `errno` IMMEDIATELY after a failed call, never delay examining it. If `system()` fails, look at `errno` again to find out why and act accordingly. – Remy Lebeau Aug 14 '14 at 19:00
  • Yes, errno can be changed by system(). One solution is assigning errno to a variable then check this variable, which can make sure that we are checking the error of opendir(). DIR *dp = opendir(a_dest_path.c_str()); if (dp) { closedir(dp); } else { int err_opendir = errno; //Then check err_opendir in below code if (errno == ENOENT) { – JackChen255 Aug 14 '14 at 19:02
  • 5
    but at the call to `system()` you already know the previous value of `errno`, it was `ENOENT`, so who cares if it changes? The `else if` on the line below the call to `system()` doesn't get tested, because the first `if` was true – Jonathan Wakely Aug 14 '14 at 19:15
  • Got it. Thanks Jonathan and Remy. – JackChen255 Aug 14 '14 at 19:18
  • one strategy could be to reset `errno` prior to any call where you would want to check it after a failure or success. In that way you could be semi-assured that the latest `errno` was related to your last operation perhaps. But what could invalidate `errno` between the call that may error and the retrieval of `errno`? Perhaps threading, and if so `errno` becomes largely useless? – Cerniuk Nov 26 '20 at 18:15
  • 1
    @WilliamCerniuk `errno` is stored on a per-thread basis, so threading is not an issue. One thread can't affect another thread's `errno`. See [Is errno thread-safe?](https://stackoverflow.com/questions/1694164/) – Remy Lebeau Nov 26 '20 at 21:01
1

In your example, as stated by others, there is no need to reset errno.

There are, however, a very few functions which return a number and for them, there is no number that can 100% represent an error occurred. In those few cases, then resetting errno is necessary.

One example of such a function is strtol(). On error it returns -1. If you really want to know whether the user passed "-1" or whether an error occurred, then you want to do:

errno = 0;
long r = strtol(input, &end, 10);
if(r == -1 && errno != 0)
    ...handle the error...

In most cases, such functions do spell out the issue in their DESCRIPTION or RETURN VALUE manual sections.

As mentioned by others, a function that returns a pointer and when it returns a nullptr it represents an error, then you do not need to reset errno since you should check that variable only if the function returned nullptr.

ptr = malloc(123);
if(ptr == nullptr)
   ...handle error, 'errno' may give you extra hints about error...
Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156