1 /* EINA - EFL data type library
2  * Copyright (C) 2007-2008 Jorge Luis Zapata Muga, Cedric Bail
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library;
16  * if not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22 
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <stdint.h>
27 
28 #include "eina_config.h"
29 #include "eina_private.h"
30 
31 
32 /* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
33 #include "eina_safety_checks.h"
34 #include "eina_error.h"
35 #include "eina_stringshare.h"
36 #include "eina_lock.h"
37 #include "eina_str.h"
38 #ifdef EINA_HAVE_THREADS
39 #include "eina_hash.h"
40 #endif
41 
42 /* TODO
43  * + add a wrapper for assert?
44  * + add common error numbers, messages
45  * + add a calltrace of errors, not only store the last error but a list of them
46  * and also store the function that set it
47  */
48 
49 /*============================================================================*
50 *                                  Local                                     *
51 *============================================================================*/
52 
53 /**
54  * @cond LOCAL
55  */
56 
57 typedef struct _Eina_Error_Message Eina_Error_Message;
58 struct _Eina_Error_Message
59 {
60    Eina_Bool string_allocated;
61    const char *string;
62 };
63 
64 #ifdef EINA_HAVE_THREADS
65 static Eina_Spinlock _eina_errno_msgs_lock;
66 static Eina_Hash *_eina_errno_msgs = NULL;
67 #endif
68 static Eina_Error_Message *_eina_errors = NULL;
69 static size_t _eina_errors_count = 0;
70 static size_t _eina_errors_allocated = 0;
71 
72 /* used to differentiate registered errors from errno.h */
73 #define EINA_ERROR_REGISTERED_BIT (1 << 30)
74 #define EINA_ERROR_REGISTERED_CHECK(err) ((err) & EINA_ERROR_REGISTERED_BIT)
75 
76 #define EINA_ERROR_FROM_INDEX(idx) ((idx) | EINA_ERROR_REGISTERED_BIT)
77 #define EINA_ERROR_TO_INDEX(err) ((err) & (~EINA_ERROR_REGISTERED_BIT))
78 
79 static Eina_Error _eina_last_error;
80 static Eina_TLS _eina_last_key;
81 
82 static Eina_Error_Message *
_eina_error_msg_alloc(void)83 _eina_error_msg_alloc(void)
84 {
85    size_t idx;
86 
87    if (_eina_errors_count == _eina_errors_allocated)
88      {
89         void *tmp;
90         size_t size;
91 
92         if (EINA_UNLIKELY(_eina_errors_allocated == 0))
93            size = 24;
94         else
95            size = _eina_errors_allocated + 8;
96 
97         tmp = realloc(_eina_errors, sizeof(Eina_Error_Message) * size);
98         if (!tmp)
99            return NULL;
100 
101         _eina_errors = tmp;
102         _eina_errors_allocated = size;
103      }
104 
105    idx = _eina_errors_count;
106    _eina_errors_count++;
107    return _eina_errors + idx;
108 }
109 
110 #ifdef _WIN32
111 # define HAVE_STRERROR_R
112 # ifdef STRERROR_R_CHAR_P
113 #  undef STRERROR_R_CHAR_P
114 # endif
115 /* Windows has strerror_s(), similar to POSIX strerror_r() */
strerror_r(int errnum,char * buf,size_t buflen)116 static inline int strerror_r(int errnum, char *buf, size_t buflen)
117 {
118    int ret;
119 
120    ret = strerror_s(buf, buflen, errnum);
121    if (strcmp(buf, "Unknown error") == 0)
122      snprintf(buf, buflen, "Unknown error %d", errnum);
123 
124    return ret;
125 }
126 #endif
127 
128 /**
129  * @endcond
130  */
131 
132 
133 /*============================================================================*
134 *                                 Global                                     *
135 *============================================================================*/
136 
137 /**
138  * @cond LOCAL
139  */
140 
141 EAPI Eina_Error EINA_ERROR_OUT_OF_MEMORY = ENOMEM;
142 
143 /**
144  * @endcond
145  */
146 
147 /**
148  * @internal
149  * @brief Initialize the error module.
150  *
151  * @return #EINA_TRUE on success, #EINA_FALSE on failure.
152  *
153  * This function sets up the error module of Eina. It is called by
154  * eina_init().
155  *
156  * This function registers the error #EINA_ERROR_OUT_OF_MEMORY.
157  *
158  * @see eina_init()
159  */
160 Eina_Bool
eina_error_init(void)161 eina_error_init(void)
162 {
163    if (!eina_tls_new(&_eina_last_key))
164      return EINA_FALSE;
165 
166 #ifdef EINA_HAVE_THREADS
167    if (!eina_spinlock_new(&_eina_errno_msgs_lock)) goto failed_lock;
168    _eina_errno_msgs = eina_hash_int32_new(EINA_FREE_CB(eina_stringshare_del));
169    if (!_eina_errno_msgs) goto failed_hash;
170 #endif
171 
172    return EINA_TRUE;
173 
174 #ifdef EINA_HAVE_THREADS
175  failed_hash:
176    eina_spinlock_free(&_eina_errno_msgs_lock);
177  failed_lock:
178    eina_tls_free(_eina_last_key);
179    _eina_last_error = 0;
180    return EINA_FALSE;
181 #endif
182 }
183 
184 /**
185  * @internal
186  * @brief Shut down the error module.
187  *
188  * @return #EINA_TRUE on success, #EINA_FALSE on failure.
189  *
190  * This function shuts down the error module set up by
191  * eina_error_init(). It is called by eina_shutdown().
192  *
193  * @see eina_shutdown()
194  */
195 Eina_Bool
eina_error_shutdown(void)196 eina_error_shutdown(void)
197 {
198    Eina_Error_Message *eem, *eem_end;
199 
200    eem = _eina_errors;
201    eem_end = eem + _eina_errors_count;
202 
203    for (; eem < eem_end; eem++)
204       if (eem->string_allocated)
205          eina_stringshare_del(eem->string);
206 
207    free(_eina_errors);
208    _eina_errors = NULL;
209    _eina_errors_count = 0;
210    _eina_errors_allocated = 0;
211 
212 #ifdef EINA_HAVE_THREADS
213    eina_hash_free(_eina_errno_msgs);
214    _eina_errno_msgs = NULL;
215    eina_spinlock_free(&_eina_errno_msgs_lock);
216 #endif
217 
218    eina_tls_free(_eina_last_key);
219    _eina_last_error = 0;
220 
221    return EINA_TRUE;
222 }
223 
224 /*============================================================================*
225 *                                   API                                      *
226 *============================================================================*/
227 
228 EAPI Eina_Error
eina_error_msg_register(const char * msg)229 eina_error_msg_register(const char *msg)
230 {
231    Eina_Error_Message *eem;
232 
233    EINA_SAFETY_ON_NULL_RETURN_VAL(msg, 0);
234 
235    eem = _eina_error_msg_alloc();
236    if (!eem)
237       return 0;
238 
239    eem->string_allocated = EINA_TRUE;
240    eem->string = eina_stringshare_add(msg);
241    if (!eem->string)
242      {
243         _eina_errors_count--;
244         return 0;
245      }
246 
247    return EINA_ERROR_FROM_INDEX(_eina_errors_count); /* identifier = index + 1 (== _count). */
248 }
249 
250 EAPI Eina_Error
eina_error_msg_static_register(const char * msg)251 eina_error_msg_static_register(const char *msg)
252 {
253    Eina_Error_Message *eem;
254 
255    EINA_SAFETY_ON_NULL_RETURN_VAL(msg, 0);
256 
257    eem = _eina_error_msg_alloc();
258    if (!eem)
259       return 0;
260 
261    eem->string_allocated = EINA_FALSE;
262    eem->string = msg;
263    return EINA_ERROR_FROM_INDEX(_eina_errors_count); /* identifier = index + 1 (== _count). */
264 }
265 
266 EAPI Eina_Bool
eina_error_msg_modify(Eina_Error error,const char * msg)267 eina_error_msg_modify(Eina_Error error, const char *msg)
268 {
269    EINA_SAFETY_ON_NULL_RETURN_VAL(msg, EINA_FALSE);
270    EINA_SAFETY_ON_FALSE_RETURN_VAL(EINA_ERROR_REGISTERED_CHECK(error), EINA_FALSE);
271    error = EINA_ERROR_TO_INDEX(error);
272    if (error < 1)
273       return EINA_FALSE;
274 
275    if ((size_t)error > _eina_errors_count)
276       return EINA_FALSE;
277 
278    if (_eina_errors[error - 1].string_allocated)
279      {
280         const char *tmp;
281 
282         if (!(tmp = eina_stringshare_add(msg)))
283            return EINA_FALSE;
284 
285         eina_stringshare_del(_eina_errors[error - 1].string);
286         _eina_errors[error - 1].string = tmp;
287         return EINA_TRUE;
288      }
289 
290    _eina_errors[error - 1].string = msg;
291    return EINA_TRUE;
292 }
293 
294 EAPI const char *
eina_error_msg_get(Eina_Error error)295 eina_error_msg_get(Eina_Error error)
296 {
297    if (!EINA_ERROR_REGISTERED_CHECK(error))
298      {
299         const char unknown_prefix[] = "Unknown error ";
300         const char *msg;
301 
302         /* original behavior of this function did not return strings
303          * for unknown errors, so skip 0 ("Success") and
304          * "Unknown error $N".
305          */
306         if (error == 0) return NULL;
307 
308 #ifndef EINA_HAVE_THREADS
309         msg = strerror(error);
310         if (strncmp(msg, unknown_prefix, sizeof(unknown_prefix) -1) == 0)
311           msg = NULL;
312 #else /* EINA_HAVE_THREADS */
313         /* strerror() is not thread safe, so use a local buffer with
314          * strerror_r() and cache resolved strings in a hash so we can
315          * return the stringshared refernece.
316          */
317         if (eina_spinlock_take(&_eina_errno_msgs_lock) != EINA_LOCK_SUCCEED)
318           {
319              EINA_SAFETY_ERROR("could not take spinlock for errno messages hash!");
320              return NULL;
321           }
322         msg = eina_hash_find(_eina_errno_msgs, &error);
323         eina_spinlock_release(&_eina_errno_msgs_lock);
324 
325         if (!msg)
326           {
327              char buf[256] = "";
328              const char *str = NULL;
329 
330 #ifdef HAVE_STRERROR_R
331 # ifndef STRERROR_R_CHAR_P
332              int ret;
333 
334              ret = strerror_r(error, buf, sizeof(buf)); /* XSI */
335              if (ret == 0)
336                str = buf;
337              else if (ret == EINVAL)
338                return NULL;
339 # else /* STRERROR_R_CHAR_P */
340              str = strerror_r(error, buf, sizeof(buf)); /* GNU */
341 # endif /* ! STRERROR_R_CHAR_P */
342 #else
343               /* not so good fallback. Usually strerror(err) will
344                * return a const string if a known error (what we use),
345                * and will return a pointer to a global modified string
346                * formatted with "Unknown error XXXX".. which we just
347                * ignore... so while it's not super-correct, this
348                * should work well.
349                */
350              eina_strlcpy(buf, strerror(error), sizeof(buf));
351              str = buf;
352 #endif /* HAVE_STRERROR_R */
353 
354              if (!str)
355                EINA_SAFETY_ERROR("strerror_r() failed");
356              else
357                {
358                   if (strncmp(str, unknown_prefix, sizeof(unknown_prefix) -1) == 0)
359                     msg = NULL;
360                   else
361                     {
362                        msg = eina_stringshare_add(str);
363                        if (eina_spinlock_take(&_eina_errno_msgs_lock) != EINA_LOCK_SUCCEED)
364                          {
365                             EINA_SAFETY_ERROR("could not take spinlock for errno messages hash!");
366                             return NULL;
367                          }
368                        eina_hash_add(_eina_errno_msgs, &error, msg);
369                        eina_spinlock_release(&_eina_errno_msgs_lock);
370                     }
371                }
372           }
373 #endif
374         return msg;
375      }
376 
377    error = EINA_ERROR_TO_INDEX(error);
378 
379    if (error < 1)
380       return NULL;
381 
382    if ((size_t)error > _eina_errors_count)
383       return NULL;
384 
385    return _eina_errors[error - 1].string;
386 }
387 
388 EAPI Eina_Error
eina_error_get(void)389 eina_error_get(void)
390 {
391    if (eina_main_loop_is())
392      return _eina_last_error;
393 
394    return (Eina_Error)(uintptr_t) eina_tls_get(_eina_last_key);
395 }
396 
397 EAPI void
eina_error_set(Eina_Error err)398 eina_error_set(Eina_Error err)
399 {
400    if (eina_main_loop_is())
401      _eina_last_error = err;
402    else
403      eina_tls_set(_eina_last_key, (void*)(uintptr_t) err);
404 }
405 
406 EAPI Eina_Error
eina_error_find(const char * msg)407 eina_error_find(const char *msg)
408 {
409    size_t i;
410 
411    EINA_SAFETY_ON_NULL_RETURN_VAL(msg, 0);
412 
413    for (i = 0; i < _eina_errors_count; i++)
414      {
415         if (_eina_errors[i].string_allocated)
416           {
417              if (_eina_errors[i].string == msg)
418                return EINA_ERROR_FROM_INDEX(i + 1);
419           }
420         if (!strcmp(_eina_errors[i].string, msg))
421           return EINA_ERROR_FROM_INDEX(i + 1);
422      }
423 
424    /* not bothering to lookup errno.h as we don't have a "maximum
425     * error", thus we'd need to loop up to some arbitrary constant and
426     * keep comparing if strerror() returns something meaningful.
427     */
428 
429    return 0;
430 }
431