blob: 667c7d91f93b5abd43b7952de30e23c3eeee0e22 [file] [log] [blame]
Benny Prijono9033e312005-11-21 02:08:39 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijono9033e312005-11-21 02:08:39 +00005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include <pj/errno.h>
Benny Prijono9055f572009-10-25 08:46:40 +000021#include <pj/log.h>
Benny Prijono9033e312005-11-21 02:08:39 +000022#include <pj/string.h>
Benny Prijonof260e462007-04-30 21:03:32 +000023#include <pj/compat/string.h>
Benny Prijono9055f572009-10-25 08:46:40 +000024#include <pj/compat/stdarg.h>
Benny Prijonodcc29522006-02-02 19:15:03 +000025#include <pj/assert.h>
Benny Prijono9033e312005-11-21 02:08:39 +000026
27/* Prototype for platform specific error message, which will be defined
28 * in separate file.
29 */
Benny Prijonof260e462007-04-30 21:03:32 +000030PJ_BEGIN_DECL
31
32 PJ_DECL(int) platform_strerror(pj_os_err_type code,
33 char *buf, pj_size_t bufsize );
34PJ_END_DECL
Benny Prijono9033e312005-11-21 02:08:39 +000035
Benny Prijonodcc29522006-02-02 19:15:03 +000036#define PJLIB_MAX_ERR_MSG_HANDLER 8
37
38/* Error message handler. */
39static unsigned err_msg_hnd_cnt;
40static struct err_msg_hnd
41{
42 pj_status_t begin;
43 pj_status_t end;
44 pj_str_t (*strerror)(pj_status_t, char*, pj_size_t);
45
46} err_msg_hnd[PJLIB_MAX_ERR_MSG_HANDLER];
47
Benny Prijono9033e312005-11-21 02:08:39 +000048/* PJLIB's own error codes/messages */
Benny Prijono3ba816e2006-03-18 12:26:55 +000049#if defined(PJ_HAS_ERROR_STRING) && PJ_HAS_ERROR_STRING!=0
Benny Prijono7eaa0fd2006-06-22 18:30:13 +000050
Benny Prijono9033e312005-11-21 02:08:39 +000051static const struct
52{
53 int code;
54 const char *msg;
55} err_str[] =
56{
Benny Prijono7eaa0fd2006-06-22 18:30:13 +000057 PJ_BUILD_ERR(PJ_EUNKNOWN, "Unknown Error" ),
58 PJ_BUILD_ERR(PJ_EPENDING, "Pending operation" ),
59 PJ_BUILD_ERR(PJ_ETOOMANYCONN, "Too many connecting sockets" ),
60 PJ_BUILD_ERR(PJ_EINVAL, "Invalid value or argument" ),
61 PJ_BUILD_ERR(PJ_ENAMETOOLONG, "Name too long" ),
62 PJ_BUILD_ERR(PJ_ENOTFOUND, "Not found" ),
63 PJ_BUILD_ERR(PJ_ENOMEM, "Not enough memory" ),
64 PJ_BUILD_ERR(PJ_EBUG, "BUG DETECTED!" ),
65 PJ_BUILD_ERR(PJ_ETIMEDOUT, "Operation timed out" ),
66 PJ_BUILD_ERR(PJ_ETOOMANY, "Too many objects of the specified type"),
67 PJ_BUILD_ERR(PJ_EBUSY, "Object is busy"),
68 PJ_BUILD_ERR(PJ_ENOTSUP, "Option/operation is not supported"),
69 PJ_BUILD_ERR(PJ_EINVALIDOP, "Invalid operation"),
70 PJ_BUILD_ERR(PJ_ECANCELLED, "Operation cancelled"),
71 PJ_BUILD_ERR(PJ_EEXISTS, "Object already exists" ),
72 PJ_BUILD_ERR(PJ_EEOF, "End of file" ),
73 PJ_BUILD_ERR(PJ_ETOOBIG, "Size is too big"),
Benny Prijonocca32812006-09-14 21:16:11 +000074 PJ_BUILD_ERR(PJ_ERESOLVE, "gethostbyname() has returned error"),
Benny Prijonod424f5b2006-09-30 11:39:17 +000075 PJ_BUILD_ERR(PJ_ETOOSMALL, "Size is too short"),
Benny Prijono1f7767b2007-10-03 18:28:49 +000076 PJ_BUILD_ERR(PJ_EIGNORED, "Ignored"),
Benny Prijono62b86eb2007-12-01 08:52:57 +000077 PJ_BUILD_ERR(PJ_EIPV6NOTSUP, "IPv6 is not supported"),
78 PJ_BUILD_ERR(PJ_EAFNOTSUP, "Unsupported address family")
Benny Prijono9033e312005-11-21 02:08:39 +000079};
Benny Prijono3ba816e2006-03-18 12:26:55 +000080#endif /* PJ_HAS_ERROR_STRING */
81
Benny Prijono9033e312005-11-21 02:08:39 +000082
83/*
84 * pjlib_error()
85 *
86 * Retrieve message string for PJLIB's own error code.
87 */
88static int pjlib_error(pj_status_t code, char *buf, pj_size_t size)
89{
Benny Prijono3ba816e2006-03-18 12:26:55 +000090#if defined(PJ_HAS_ERROR_STRING) && PJ_HAS_ERROR_STRING!=0
Benny Prijono9033e312005-11-21 02:08:39 +000091 unsigned i;
92
93 for (i=0; i<sizeof(err_str)/sizeof(err_str[0]); ++i) {
94 if (err_str[i].code == code) {
95 pj_size_t len = strlen(err_str[i].msg);
96 if (len >= size) len = size-1;
97 pj_memcpy(buf, err_str[i].msg, len);
98 buf[len] = '\0';
99 return len;
100 }
101 }
Benny Prijono3ba816e2006-03-18 12:26:55 +0000102#endif
Benny Prijono9033e312005-11-21 02:08:39 +0000103
Benny Prijono3ba816e2006-03-18 12:26:55 +0000104 return pj_ansi_snprintf( buf, size, "Unknown pjlib error %d", code);
Benny Prijono9033e312005-11-21 02:08:39 +0000105}
106
Benny Prijonodcc29522006-02-02 19:15:03 +0000107#define IN_RANGE(val,start,end) ((val)>=(start) && (val)<(end))
108
109/* Register strerror handle. */
Benny Prijono8ab968f2007-07-20 08:08:30 +0000110PJ_DEF(pj_status_t) pj_register_strerror( pj_status_t start,
Benny Prijonodcc29522006-02-02 19:15:03 +0000111 pj_status_t space,
Benny Prijonoa7b376b2008-01-25 16:06:33 +0000112 pj_error_callback f)
Benny Prijonodcc29522006-02-02 19:15:03 +0000113{
114 unsigned i;
115
116 /* Check arguments. */
117 PJ_ASSERT_RETURN(start && space && f, PJ_EINVAL);
118
119 /* Check if there aren't too many handlers registered. */
120 PJ_ASSERT_RETURN(err_msg_hnd_cnt < PJ_ARRAY_SIZE(err_msg_hnd),
121 PJ_ETOOMANY);
122
123 /* Start error must be greater than PJ_ERRNO_START_USER */
124 PJ_ASSERT_RETURN(start >= PJ_ERRNO_START_USER, PJ_EEXISTS);
125
126 /* Check that no existing handler has covered the specified range. */
127 for (i=0; i<err_msg_hnd_cnt; ++i) {
128 if (IN_RANGE(start, err_msg_hnd[i].begin, err_msg_hnd[i].end) ||
129 IN_RANGE(start+space-1, err_msg_hnd[i].begin, err_msg_hnd[i].end))
130 {
131 return PJ_EEXISTS;
132 }
133 }
134
135 /* Register the handler. */
136 err_msg_hnd[err_msg_hnd_cnt].begin = start;
137 err_msg_hnd[err_msg_hnd_cnt].end = start + space;
138 err_msg_hnd[err_msg_hnd_cnt].strerror = f;
139
140 ++err_msg_hnd_cnt;
141
142 return PJ_SUCCESS;
143}
144
Benny Prijonof762ee72006-12-01 11:14:37 +0000145/* Internal PJLIB function called by pj_shutdown() to clear error handlers */
146void pj_errno_clear_handlers(void)
147{
148 err_msg_hnd_cnt = 0;
149 pj_bzero(err_msg_hnd, sizeof(err_msg_hnd));
150}
151
152
Benny Prijono9033e312005-11-21 02:08:39 +0000153/*
154 * pj_strerror()
155 */
156PJ_DEF(pj_str_t) pj_strerror( pj_status_t statcode,
157 char *buf, pj_size_t bufsize )
158{
159 int len = -1;
160 pj_str_t errstr;
161
Benny Prijonodcc29522006-02-02 19:15:03 +0000162 pj_assert(buf && bufsize);
163
Benny Prijonod0a35852007-02-23 01:07:54 +0000164 if (statcode == PJ_SUCCESS) {
165 len = pj_ansi_snprintf( buf, bufsize, "Success");
166
167 } else if (statcode < PJ_ERRNO_START + PJ_ERRNO_SPACE_SIZE) {
Benny Prijonoed811d72006-03-10 12:57:12 +0000168 len = pj_ansi_snprintf( buf, bufsize, "Unknown error %d", statcode);
Benny Prijono9033e312005-11-21 02:08:39 +0000169
170 } else if (statcode < PJ_ERRNO_START_STATUS + PJ_ERRNO_SPACE_SIZE) {
171 len = pjlib_error(statcode, buf, bufsize);
172
173 } else if (statcode < PJ_ERRNO_START_SYS + PJ_ERRNO_SPACE_SIZE) {
174 len = platform_strerror(PJ_STATUS_TO_OS(statcode), buf, bufsize);
175
Benny Prijono9033e312005-11-21 02:08:39 +0000176 } else {
Benny Prijonodcc29522006-02-02 19:15:03 +0000177 unsigned i;
Benny Prijono9033e312005-11-21 02:08:39 +0000178
Benny Prijonodcc29522006-02-02 19:15:03 +0000179 /* Find user handler to get the error message. */
180 for (i=0; i<err_msg_hnd_cnt; ++i) {
181 if (IN_RANGE(statcode, err_msg_hnd[i].begin, err_msg_hnd[i].end)) {
182 return (*err_msg_hnd[i].strerror)(statcode, buf, bufsize);
183 }
184 }
Benny Prijonod424f5b2006-09-30 11:39:17 +0000185
186 /* Handler not found! */
187 len = pj_ansi_snprintf( buf, bufsize, "Unknown error %d", statcode);
Benny Prijono9033e312005-11-21 02:08:39 +0000188 }
189
190 if (len < 1) {
191 *buf = '\0';
192 len = 0;
193 }
194
195 errstr.ptr = buf;
196 errstr.slen = len;
197
198 return errstr;
199}
200
Benny Prijono9055f572009-10-25 08:46:40 +0000201#if PJ_LOG_MAX_LEVEL >= 1
Benny Prijonoc6768e52009-11-09 04:09:13 +0000202static void invoke_log(const char *sender, int level, const char *format, ...)
Benny Prijono9055f572009-10-25 08:46:40 +0000203{
204 va_list arg;
205 va_start(arg, format);
206 pj_log(sender, level, format, arg);
207 va_end(arg);
208}
209
Benny Prijonoc6768e52009-11-09 04:09:13 +0000210static void pj_perror_imp(int log_level, const char *sender,
211 pj_status_t status,
212 const char *title_fmt, va_list marker)
Benny Prijono9055f572009-10-25 08:46:40 +0000213{
Benny Prijonoc6768e52009-11-09 04:09:13 +0000214 char titlebuf[PJ_PERROR_TITLE_BUF_SIZE];
Benny Prijono9055f572009-10-25 08:46:40 +0000215 char errmsg[PJ_ERR_MSG_SIZE];
Benny Prijonoc6768e52009-11-09 04:09:13 +0000216 int len;
Benny Prijono9055f572009-10-25 08:46:40 +0000217
Benny Prijonoc6768e52009-11-09 04:09:13 +0000218 /* Build the title */
219 len = pj_ansi_vsnprintf(titlebuf, sizeof(titlebuf), title_fmt, marker);
220 if (len < 0 || len >= sizeof(titlebuf))
221 pj_ansi_strcpy(titlebuf, "Error");
Benny Prijono9055f572009-10-25 08:46:40 +0000222
Benny Prijonoc6768e52009-11-09 04:09:13 +0000223 /* Get the error */
Benny Prijono9055f572009-10-25 08:46:40 +0000224 pj_strerror(status, errmsg, sizeof(errmsg));
Benny Prijonoc6768e52009-11-09 04:09:13 +0000225
226 /* Send to log */
227 invoke_log(sender, log_level, "%s: %s", titlebuf, errmsg);
228}
229
230PJ_DEF(void) pj_perror(int log_level, const char *sender, pj_status_t status,
231 const char *title_fmt, ...)
232{
233 va_list marker;
234 va_start(marker, title_fmt);
235 pj_perror_imp(log_level, sender, status, title_fmt, marker);
236 va_end(marker);
237}
238
239PJ_DEF(void) pj_perror_1(const char *sender, pj_status_t status,
240 const char *title_fmt, ...)
241{
242 va_list marker;
243 va_start(marker, title_fmt);
244 pj_perror_imp(1, sender, status, title_fmt, marker);
245 va_end(marker);
246}
247
248#else /* #if PJ_LOG_MAX_LEVEL >= 1 */
249PJ_DEF(void) pj_perror(int log_level, const char *sender, pj_status_t status,
250 const char *title_fmt, ...)
251{
Benny Prijono9055f572009-10-25 08:46:40 +0000252}
253#endif /* #if PJ_LOG_MAX_LEVEL >= 1 */
254
255
Benny Prijonoc6768e52009-11-09 04:09:13 +0000256#if PJ_LOG_MAX_LEVEL >= 2
257PJ_DEF(void) pj_perror_2(const char *sender, pj_status_t status,
258 const char *title_fmt, ...)
259{
260 va_list marker;
261 va_start(marker, title_fmt);
262 pj_perror_imp(2, sender, status, title_fmt, marker);
263 va_end(marker);
264}
265#endif
266
267#if PJ_LOG_MAX_LEVEL >= 3
268PJ_DEF(void) pj_perror_3(const char *sender, pj_status_t status,
269 const char *title_fmt, ...)
270{
271 va_list marker;
272 va_start(marker, title_fmt);
273 pj_perror_imp(3, sender, status, title_fmt, marker);
274 va_end(marker);
275}
276#endif
277
278#if PJ_LOG_MAX_LEVEL >= 4
279PJ_DEF(void) pj_perror_4(const char *sender, pj_status_t status,
280 const char *title_fmt, ...)
281{
282 va_list marker;
283 va_start(marker, title_fmt);
284 pj_perror_imp(4, sender, status, title_fmt, marker);
285 va_end(marker);
286}
287#endif
288
289#if PJ_LOG_MAX_LEVEL >= 5
290PJ_DEF(void) pj_perror_5(const char *sender, pj_status_t status,
291 const char *title_fmt, ...)
292{
293 va_list marker;
294 va_start(marker, title_fmt);
295 pj_perror_imp(5, sender, status, title_fmt, marker);
296 va_end(marker);
297}
298#endif
299
300#if PJ_LOG_MAX_LEVEL >= 6
301PJ_DEF(void) pj_perror_6(const char *sender, pj_status_t status,
302 const char *title_fmt, ...)
303{
304 va_list marker;
305 va_start(marker, title_fmt);
306 pj_perror_imp(6, sender, status, title_fmt, marker);
307 va_end(marker);
308}
309#endif
310