| 1 | /* |
| 2 | * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"). |
| 5 | * You may not use this file except in compliance with the License. |
| 6 | * A copy of the License is located at |
| 7 | * |
| 8 | * http://aws.amazon.com/apache2.0 |
| 9 | * |
| 10 | * or in the "license" file accompanying this file. This file is distributed |
| 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either |
| 12 | * express or implied. See the License for the specific language governing |
| 13 | * permissions and limitations under the License. |
| 14 | */ |
| 15 | |
| 16 | #include <aws/common/error.h> |
| 17 | |
| 18 | #include <aws/common/common.h> |
| 19 | |
| 20 | #include <errno.h> |
| 21 | #include <stdio.h> |
| 22 | #include <stdlib.h> |
| 23 | |
| 24 | static AWS_THREAD_LOCAL int tl_last_error = 0; |
| 25 | |
| 26 | static aws_error_handler_fn *s_global_handler = NULL; |
| 27 | static void *s_global_error_context = NULL; |
| 28 | |
| 29 | static AWS_THREAD_LOCAL aws_error_handler_fn *tl_thread_handler = NULL; |
| 30 | AWS_THREAD_LOCAL void *tl_thread_handler_context = NULL; |
| 31 | |
| 32 | #ifndef AWS_MAX_ERROR_SLOTS |
| 33 | # define AWS_MAX_ERROR_SLOTS 16 |
| 34 | #endif |
| 35 | |
| 36 | /* Since slot size is 00000100 00000000, to divide, we need to shift right by 10 |
| 37 | * bits to find the slot, and to find the modulus, we use a binary and with |
| 38 | * 00000011 11111111 to find the index in that slot. The next three values |
| 39 | * define those constants */ |
| 40 | #define AWS_ERROR_SLOT_SIZE 0x0400 |
| 41 | #define SLOT_DIV_SHIFT 0x0A |
| 42 | #define SLOT_MASK 0x03FF |
| 43 | |
| 44 | static const int MAX_ERROR_CODE = AWS_ERROR_SLOT_SIZE * AWS_MAX_ERROR_SLOTS; |
| 45 | |
| 46 | static const struct aws_error_info_list *volatile ERROR_SLOTS[AWS_MAX_ERROR_SLOTS] = {0}; |
| 47 | |
| 48 | int aws_last_error(void) { |
| 49 | return tl_last_error; |
| 50 | } |
| 51 | |
| 52 | static const struct aws_error_info *get_error_by_code(int err) { |
| 53 | if (err >= MAX_ERROR_CODE || err < 0) { |
| 54 | return NULL; |
| 55 | } |
| 56 | |
| 57 | int slot_index = err >> SLOT_DIV_SHIFT; |
| 58 | int error_index = err & SLOT_MASK; |
| 59 | |
| 60 | const struct aws_error_info_list *error_slot = ERROR_SLOTS[slot_index]; |
| 61 | |
| 62 | if (!error_slot || error_index >= error_slot->count) { |
| 63 | return NULL; |
| 64 | } |
| 65 | |
| 66 | return &error_slot->error_list[error_index]; |
| 67 | } |
| 68 | |
| 69 | const char *aws_error_str(int err) { |
| 70 | const struct aws_error_info *error_info = get_error_by_code(err); |
| 71 | |
| 72 | if (error_info) { |
| 73 | return error_info->error_str; |
| 74 | } |
| 75 | |
| 76 | return "Unknown Error Code" ; |
| 77 | } |
| 78 | |
| 79 | const char *aws_error_name(int err) { |
| 80 | const struct aws_error_info *error_info = get_error_by_code(err); |
| 81 | |
| 82 | if (error_info) { |
| 83 | return error_info->literal_name; |
| 84 | } |
| 85 | |
| 86 | return "Unknown Error Code" ; |
| 87 | } |
| 88 | |
| 89 | const char *aws_error_lib_name(int err) { |
| 90 | const struct aws_error_info *error_info = get_error_by_code(err); |
| 91 | |
| 92 | if (error_info) { |
| 93 | return error_info->lib_name; |
| 94 | } |
| 95 | |
| 96 | return "Unknown Error Code" ; |
| 97 | } |
| 98 | |
| 99 | const char *aws_error_debug_str(int err) { |
| 100 | const struct aws_error_info *error_info = get_error_by_code(err); |
| 101 | |
| 102 | if (error_info) { |
| 103 | return error_info->formatted_name; |
| 104 | } |
| 105 | |
| 106 | return "Unknown Error Code" ; |
| 107 | } |
| 108 | |
| 109 | void aws_raise_error_private(int err) { |
| 110 | tl_last_error = err; |
| 111 | |
| 112 | if (tl_thread_handler) { |
| 113 | tl_thread_handler(tl_last_error, tl_thread_handler_context); |
| 114 | } else if (s_global_handler) { |
| 115 | s_global_handler(tl_last_error, s_global_error_context); |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | void aws_reset_error(void) { |
| 120 | tl_last_error = 0; |
| 121 | } |
| 122 | |
| 123 | void aws_restore_error(int err) { |
| 124 | tl_last_error = err; |
| 125 | } |
| 126 | |
| 127 | aws_error_handler_fn *aws_set_global_error_handler_fn(aws_error_handler_fn *handler, void *ctx) { |
| 128 | aws_error_handler_fn *old_handler = s_global_handler; |
| 129 | s_global_handler = handler; |
| 130 | s_global_error_context = ctx; |
| 131 | |
| 132 | return old_handler; |
| 133 | } |
| 134 | |
| 135 | aws_error_handler_fn *aws_set_thread_local_error_handler_fn(aws_error_handler_fn *handler, void *ctx) { |
| 136 | aws_error_handler_fn *old_handler = tl_thread_handler; |
| 137 | tl_thread_handler = handler; |
| 138 | tl_thread_handler_context = ctx; |
| 139 | |
| 140 | return old_handler; |
| 141 | } |
| 142 | |
| 143 | void aws_register_error_info(const struct aws_error_info_list *error_info) { |
| 144 | /* |
| 145 | * We're not so worried about these asserts being removed in an NDEBUG build |
| 146 | * - we'll either segfault immediately (for the first two) or for the count |
| 147 | * assert, the registration will be ineffective. |
| 148 | */ |
| 149 | AWS_FATAL_ASSERT(error_info); |
| 150 | AWS_FATAL_ASSERT(error_info->error_list); |
| 151 | AWS_FATAL_ASSERT(error_info->count); |
| 152 | |
| 153 | const int min_range = error_info->error_list[0].error_code; |
| 154 | const int slot_index = min_range >> SLOT_DIV_SHIFT; |
| 155 | |
| 156 | if (slot_index >= AWS_MAX_ERROR_SLOTS || slot_index < 0) { |
| 157 | /* This is an NDEBUG build apparently. Kill the process rather than |
| 158 | * corrupting heap. */ |
| 159 | fprintf(stderr, "Bad error slot index %d\n" , slot_index); |
| 160 | AWS_FATAL_ASSERT(false); |
| 161 | } |
| 162 | |
| 163 | #if DEBUG_BUILD |
| 164 | /* Assert that error info entries are in the right order. */ |
| 165 | for (int i = 1; i < error_info->count; ++i) { |
| 166 | const int expected_code = min_range + i; |
| 167 | const struct aws_error_info *info = &error_info->error_list[i]; |
| 168 | if (info->error_code != expected_code) { |
| 169 | if (info->error_code) { |
| 170 | fprintf(stderr, "Error %s is at wrong index of error info list.\n" , info->literal_name); |
| 171 | } else { |
| 172 | fprintf(stderr, "Error %d is missing from error info list.\n" , expected_code); |
| 173 | } |
| 174 | AWS_FATAL_ASSERT(0); |
| 175 | } |
| 176 | } |
| 177 | #endif /* DEBUG_BUILD */ |
| 178 | |
| 179 | ERROR_SLOTS[slot_index] = error_info; |
| 180 | } |
| 181 | |
| 182 | void aws_unregister_error_info(const struct aws_error_info_list *error_info) { |
| 183 | AWS_FATAL_ASSERT(error_info); |
| 184 | AWS_FATAL_ASSERT(error_info->error_list); |
| 185 | AWS_FATAL_ASSERT(error_info->count); |
| 186 | |
| 187 | const int min_range = error_info->error_list[0].error_code; |
| 188 | const int slot_index = min_range >> SLOT_DIV_SHIFT; |
| 189 | |
| 190 | if (slot_index >= AWS_MAX_ERROR_SLOTS || slot_index < 0) { |
| 191 | /* This is an NDEBUG build apparently. Kill the process rather than |
| 192 | * corrupting heap. */ |
| 193 | fprintf(stderr, "Bad error slot index %d\n" , slot_index); |
| 194 | AWS_FATAL_ASSERT(0); |
| 195 | } |
| 196 | |
| 197 | ERROR_SLOTS[slot_index] = NULL; |
| 198 | } |
| 199 | |
| 200 | int aws_translate_and_raise_io_error(int error_no) { |
| 201 | switch (error_no) { |
| 202 | case EINVAL: |
| 203 | return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); |
| 204 | case ESPIPE: |
| 205 | return aws_raise_error(AWS_ERROR_STREAM_UNSEEKABLE); |
| 206 | case EPERM: |
| 207 | case EACCES: |
| 208 | return aws_raise_error(AWS_ERROR_NO_PERMISSION); |
| 209 | case EISDIR: |
| 210 | case ENAMETOOLONG: |
| 211 | case ENOENT: |
| 212 | return aws_raise_error(AWS_ERROR_FILE_INVALID_PATH); |
| 213 | case ENFILE: |
| 214 | return aws_raise_error(AWS_ERROR_MAX_FDS_EXCEEDED); |
| 215 | case ENOMEM: |
| 216 | return aws_raise_error(AWS_ERROR_OOM); |
| 217 | default: |
| 218 | return aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE); |
| 219 | } |
| 220 | } |
| 221 | |