# C Error Handling: errno

最近碰到的坑，但查了下發現是基礎概念，整理記錄一下。

## 背景: `gmtime_r`

他會從傳進去的 `time_t` 算出年月日、星期、時分秒。這並不是件簡單的事情，除了閏年外，假如考慮進時區的話，還要計入日光節約時間，會需要讀取一些定期更新的檔案。

`gmtime_r` 的細節不是這文章的重點，只在這裡大概說一下它的功能。

## 使用並且處理

```cpp
#include <ctime>
#include <cstdio>

#include <errno.h>

int main() {
    time_t tt = 0;
    
    std::tm tm0;
    errno = 0;
    gmtime_r(&tt, &tm0);
    if (errno != 0) {
        perror("gmtime_r");
    }
    return 0;
}
```

這在 docker ubuntu 16.04 上會遇到：

```plaintext
gmtime_r: No such file or directory
```

但是在非 docker 的 ubuntu 16.04 或者 docker ubuntu 20.04 或者 MacOS。

一開始，我以爲可能是類似 `timedatectl` 的問題，因為他也有類似的現象：

```plaintext
root@618d495a44a6:~# timedatectl status
Failed to create bus connection: No such file or directory
```

在中間過程他似乎會去尋找某個檔案。雖然這有點亂槍打鳥，但有沒有可能他們用到相同的檔案？

所以我嘗試了[這個做法](https://askubuntu.com/a/1307706/1007979)，雖然 `timedatectl` 可行，但最上面的程式仍然會噴錯誤。

## `errno` 的原則和 `gmtime_r` 的文件

後來，我重新翻了 APUE 的最前面，關於 errno 的必需要知道的事情，才發現我大概錯誤的使用了 errno：

1. 函數並不會主動清乾淨 errno
    
2. 只有在函數**告訴你有錯**（例如回傳的指標 spec 上說明錯誤會是 NULL），才可以去檢查 errno (或者說，這時 errno 才有意義)
    

也就是說，有可能一個系統函數設計這樣：讀取某個固定路徑的檔案，假如該檔案沒有，就用其他預設值，不需要回傳錯誤。

在沒有該檔案時， errno 會被 open 設置，但是函數並不會在回傳預設值前清乾淨 errno。

最後，根據 `gmtime_r` 的[文件](https://en.cppreference.com/w/c/chrono/gmtime)，直接判斷回傳指標是否為 NULL 才是正確的作法。

```cpp
#include <ctime>
#include <cstdio>

#include <errno.h>

int main() {
    time_t tt = 0;
    std::tm tm0;
    if (!gmtime_r(&tt, &tm0)) {
        perror("gmtime_r");
    }
    return 0;
}
```
