text: /*
   * Copyright 2020 Google LLC
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  #include "ecclesia/lib/status/posix.h"
  
  #include <errno.h>
  
  #include <cstring>
  #include <string>
  
  #include "absl/status/status.h"
  #include "absl/strings/str_format.h"
  #include "absl/strings/string_view.h"
  
  namespace ecclesia {
  
  absl::StatusCode ErrnoToStatusCode(int error_number) {
    switch (error_number) {
      case 0:
        return absl::StatusCode::kOk;
      case EINVAL:        // Invalid argument
      case ENAMETOOLONG:  // Filename too long
      case E2BIG:         // Argument list too long
      case EDESTADDRREQ:  // Destination address required
      case EDOM:          // Mathematics argument out of domain of function
      case EFAULT:        // Bad address
      case EILSEQ:        // Illegal byte sequence
      case ENOPROTOOPT:   // Protocol not available
      case ENOSTR:        // Not a STREAM
      case ENOTSOCK:      // Not a socket
      case ENOTTY:        // Inappropriate I/O control operation
      case EPROTOTYPE:    // Protocol wrong type for socket
      case ESPIPE:        // Invalid seek
        return absl::StatusCode::kInvalidArgument;
  
      case ETIMEDOUT:  // Connection timed out
      case ETIME:      // Timer expired
        return absl::StatusCode::kDeadlineExceeded;
      case ENODEV:  // No such device
      case ENOENT:  // No such file or directory
  #ifdef ENOMEDIUM
      case ENOMEDIUM:  // No medium found
  #endif
      case ENXIO:  // No such device or address
      case ESRCH:  // No such process
        return absl::StatusCode::kNotFound;
      case EEXIST:         // File exists
      case EADDRNOTAVAIL:  // Address not available
      case EALREADY:       // Connection already in progress
  #ifdef ENOTUNIQ
      case ENOTUNIQ:  // Name not unique on network
  #endif
        return absl::StatusCode::kAlreadyExists;
      case EPERM:   // Operation not permitted
      case EACCES:  // Permission denied
  #ifdef ENOKEY
      case ENOKEY:  // Required key not available
  #endif
      case EROFS:  // Read only file system
        return absl::StatusCode::kPermissionDenied;
      case ENOTEMPTY:   // Directory not empty
      case EISDIR:      // Is a directory
      case ENOTDIR:     // Not a directory
      case EADDRINUSE:  // Address already in use
      case EBADF:       // Invalid file descriptor
  #ifdef EBADFD
      case EBADFD:  // File descriptor in bad state
  #endif
      case EBUSY:    // Device or resource busy
      case ECHILD:   // No child processes
      case EISCONN:  // Socket is connected
  #ifdef EISNAM
      case EISNAM:  // Is a named type file
  #endif
  #ifdef ENOTBLK
      case ENOTBLK:  // Block device required
  #endif
      case ENOTCONN:  // The socket is not connected
      case EPIPE:     // Broken pipe
  #ifdef ESHUTDOWN
      case ESHUTDOWN:  // Cannot send after transport endpoint shutdown
  #endif
      case ETXTBSY:  // Text file busy
  #ifdef EUNATCH
      case EUNATCH:  // Protocol driver not attached
  #endif
        return absl::StatusCode::kFailedPrecondition;
      case ENOSPC:  // No space left on device
  #ifdef EDQUOT
      case EDQUOT:  // Disk quota exceeded
  #endif
      case EMFILE:   // Too many open files
      case EMLINK:   // Too many links
      case ENFILE:   // Too many open files in system
      case ENOBUFS:  // No buffer space available
      case ENODATA:  // No message is available on the STREAM read queue
      case ENOMEM:   // Not enough space
      case ENOSR:    // No STREAM resources
  #ifdef EUSERS
      case EUSERS:  // Too many users
  #endif
        return absl::StatusCode::kResourceExhausted;
  #ifdef ECHRNG
      case ECHRNG:  // Channel number out of range
  #endif
      case EFBIG:      // File too large
      case EOVERFLOW:  // Value too large to be stored in data type
      case ERANGE:     // Result too large
        return absl::StatusCode::kOutOfRange;
  #ifdef ENOPKG
      case ENOPKG:  // Package not installed
  #endif
      case ENOSYS:        // Function not implemented
      case ENOTSUP:       // Operation not supported
      case EAFNOSUPPORT:  // Address family not supported
  #ifdef EPFNOSUPPORT
      case EPFNOSUPPORT:  // Protocol family not supported
  #endif
      case EPROTONOSUPPORT:  // Protocol not supported
  #ifdef ESOCKTNOSUPPORT
      case ESOCKTNOSUPPORT:  // Socket type not supported
  #endif
      case EXDEV:  // Improper link
        return absl::StatusCode::kUnimplemented;
      case EAGAIN:  // Resource temporarily unavailable
  #ifdef ECOMM
      case ECOMM:  // Communication error on send
  #endif
      case ECONNREFUSED:  // Connection refused
      case ECONNABORTED:  // Connection aborted
      case ECONNRESET:    // Connection reset
      case EINTR:         // Interrupted function call
  #ifdef EHOSTDOWN
      case EHOSTDOWN:  // Host is down
  #endif
      case EHOSTUNREACH:  // Host is unreachable
      case ENETDOWN:      // Network is down
      case ENETRESET:     // Connection aborted by network
      case ENETUNREACH:   // Network unreachable
      case ENOLCK:        // No locks available
      case ENOLINK:       // Link has been severed
  #ifdef ENONET
      case ENONET:  // Machine is not on the network
  #endif
        return absl::StatusCode::kUnavailable;
      case EDEADLK:  // Resource deadlock avoided
  #ifdef ESTALE
      case ESTALE:  // Stale file handle
  #endif
        return absl::StatusCode::kAborted;
      case ECANCELED:  // Operation cancelled
        return absl::StatusCode::kCancelled;
      default:
        return absl::StatusCode::kUnknown;
    }
  }
  
  absl::Status PosixErrorToStatus(absl::string_view message, int error_number) {
    // Use absl::StrError once it is available in public API:
    // https://github.com/abseil/abseil-cpp/issues/942
    // Assume GNU semantics for strerror_r, which returns char*.
    char buf[1024] = {'\000'};
    const char *ret = strerror_r(error_number, buf, sizeof(buf));
    std::string errno_msg;
    if (buf[0] != '\000') {
      errno_msg = "strerror failed";
    } else {
      errno_msg = std::string(ret);
    }
    return absl::Status(
        ErrnoToStatusCode(error_number),
        absl::StrFormat("[errno: %d (%s)] %s", errno, errno_msg, message));
  }
  
  }  // namespace ecclesia
  