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
24static AWS_THREAD_LOCAL int tl_last_error = 0;
25
26static aws_error_handler_fn *s_global_handler = NULL;
27static void *s_global_error_context = NULL;
28
29static AWS_THREAD_LOCAL aws_error_handler_fn *tl_thread_handler = NULL;
30AWS_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
44static const int MAX_ERROR_CODE = AWS_ERROR_SLOT_SIZE * AWS_MAX_ERROR_SLOTS;
45
46static const struct aws_error_info_list *volatile ERROR_SLOTS[AWS_MAX_ERROR_SLOTS] = {0};
47
48int aws_last_error(void) {
49 return tl_last_error;
50}
51
52static 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
69const 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
79const 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
89const 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
99const 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
109void 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
119void aws_reset_error(void) {
120 tl_last_error = 0;
121}
122
123void aws_restore_error(int err) {
124 tl_last_error = err;
125}
126
127aws_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
135aws_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
143void 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
182void 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
200int 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