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 | |