1 /* $Id$ */
2 /*
3  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5  *
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>
21 #include <pj/log.h>
22 #include <pj/string.h>
23 #include <pj/compat/string.h>
24 #include <pj/compat/stdarg.h>
25 #include <pj/assert.h>
26 
27 /* Prototype for platform specific error message, which will be defined
28  * in separate file.
29  */
30 PJ_BEGIN_DECL
31 
32     PJ_DECL(int) platform_strerror(pj_os_err_type code,
33                               	   char *buf, pj_size_t bufsize );
34 PJ_END_DECL
35 
36 #ifndef PJLIB_MAX_ERR_MSG_HANDLER
37 #	define PJLIB_MAX_ERR_MSG_HANDLER   10
38 #endif
39 
40 /* Error message handler. */
41 static unsigned err_msg_hnd_cnt;
42 static struct err_msg_hnd
43 {
44     pj_status_t	    begin;
45     pj_status_t	    end;
46     pj_str_t	  (*strerror)(pj_status_t, char*, pj_size_t);
47 
48 } err_msg_hnd[PJLIB_MAX_ERR_MSG_HANDLER];
49 
50 /* PJLIB's own error codes/messages */
51 #if defined(PJ_HAS_ERROR_STRING) && PJ_HAS_ERROR_STRING!=0
52 
53 static const struct
54 {
55     int code;
56     const char *msg;
57 } err_str[] =
58 {
59     PJ_BUILD_ERR(PJ_EUNKNOWN,      "Unknown Error" ),
60     PJ_BUILD_ERR(PJ_EPENDING,      "Pending operation" ),
61     PJ_BUILD_ERR(PJ_ETOOMANYCONN,  "Too many connecting sockets" ),
62     PJ_BUILD_ERR(PJ_EINVAL,        "Invalid value or argument" ),
63     PJ_BUILD_ERR(PJ_ENAMETOOLONG,  "Name too long" ),
64     PJ_BUILD_ERR(PJ_ENOTFOUND,     "Not found" ),
65     PJ_BUILD_ERR(PJ_ENOMEM,        "Not enough memory" ),
66     PJ_BUILD_ERR(PJ_EBUG,          "BUG DETECTED!" ),
67     PJ_BUILD_ERR(PJ_ETIMEDOUT,     "Operation timed out" ),
68     PJ_BUILD_ERR(PJ_ETOOMANY,      "Too many objects of the specified type"),
69     PJ_BUILD_ERR(PJ_EBUSY,         "Object is busy"),
70     PJ_BUILD_ERR(PJ_ENOTSUP,	   "Option/operation is not supported"),
71     PJ_BUILD_ERR(PJ_EINVALIDOP,	   "Invalid operation"),
72     PJ_BUILD_ERR(PJ_ECANCELLED,    "Operation cancelled"),
73     PJ_BUILD_ERR(PJ_EEXISTS,       "Object already exists" ),
74     PJ_BUILD_ERR(PJ_EEOF,	   "End of file" ),
75     PJ_BUILD_ERR(PJ_ETOOBIG,	   "Size is too big"),
76     PJ_BUILD_ERR(PJ_ERESOLVE,	   "gethostbyname() has returned error"),
77     PJ_BUILD_ERR(PJ_ETOOSMALL,	   "Size is too short"),
78     PJ_BUILD_ERR(PJ_EIGNORED,	   "Ignored"),
79     PJ_BUILD_ERR(PJ_EIPV6NOTSUP,   "IPv6 is not supported"),
80     PJ_BUILD_ERR(PJ_EAFNOTSUP,	   "Unsupported address family"),
81     PJ_BUILD_ERR(PJ_EGONE,	   "Object no longer exists"),
82     PJ_BUILD_ERR(PJ_ESOCKETSTOP,   "Socket is in bad state")
83 };
84 #endif	/* PJ_HAS_ERROR_STRING */
85 
86 
87 /*
88  * pjlib_error()
89  *
90  * Retrieve message string for PJLIB's own error code.
91  */
pjlib_error(pj_status_t code,char * buf,pj_size_t size)92 static int pjlib_error(pj_status_t code, char *buf, pj_size_t size)
93 {
94     int len;
95 
96 #if defined(PJ_HAS_ERROR_STRING) && PJ_HAS_ERROR_STRING!=0
97     unsigned i;
98 
99     for (i=0; i<sizeof(err_str)/sizeof(err_str[0]); ++i) {
100         if (err_str[i].code == code) {
101             pj_size_t len2 = pj_ansi_strlen(err_str[i].msg);
102             if (len2 >= size) len2 = size-1;
103             pj_memcpy(buf, err_str[i].msg, len2);
104             buf[len2] = '\0';
105             return (int)len2;
106         }
107     }
108 #endif
109 
110     len = pj_ansi_snprintf( buf, size, "Unknown pjlib error %d", code);
111     if (len < 1 || len >= (int)size)
112 	len = (int)(size - 1);
113     return len;
114 }
115 
116 #define IN_RANGE(val,start,end)	    ((val)>=(start) && (val)<(end))
117 
118 /* Register strerror handle. */
pj_register_strerror(pj_status_t start,pj_status_t space,pj_error_callback f)119 PJ_DEF(pj_status_t) pj_register_strerror( pj_status_t start,
120 					  pj_status_t space,
121 					  pj_error_callback f)
122 {
123     unsigned i;
124 
125     /* Check arguments. */
126     PJ_ASSERT_RETURN(start && space && f, PJ_EINVAL);
127 
128     /* Check if there aren't too many handlers registered. */
129     PJ_ASSERT_RETURN(err_msg_hnd_cnt < PJ_ARRAY_SIZE(err_msg_hnd),
130 		     PJ_ETOOMANY);
131 
132     /* Start error must be greater than PJ_ERRNO_START_USER */
133     PJ_ASSERT_RETURN(start >= PJ_ERRNO_START_USER, PJ_EEXISTS);
134 
135     /* Check that no existing handler has covered the specified range. */
136     for (i=0; i<err_msg_hnd_cnt; ++i) {
137 	if (IN_RANGE(start, err_msg_hnd[i].begin, err_msg_hnd[i].end) ||
138 	    IN_RANGE(start+space-1, err_msg_hnd[i].begin, err_msg_hnd[i].end))
139 	{
140 	    if (err_msg_hnd[i].begin == start &&
141 		err_msg_hnd[i].end == (start+space) &&
142 		err_msg_hnd[i].strerror == f)
143 	    {
144 		/* The same range and handler has already been registered */
145 		return PJ_SUCCESS;
146 	    }
147 
148 	    return PJ_EEXISTS;
149 	}
150     }
151 
152     /* Register the handler. */
153     err_msg_hnd[err_msg_hnd_cnt].begin = start;
154     err_msg_hnd[err_msg_hnd_cnt].end = start + space;
155     err_msg_hnd[err_msg_hnd_cnt].strerror = f;
156 
157     ++err_msg_hnd_cnt;
158 
159     return PJ_SUCCESS;
160 }
161 
162 /* Internal PJLIB function called by pj_shutdown() to clear error handlers */
pj_errno_clear_handlers(void)163 void pj_errno_clear_handlers(void)
164 {
165     err_msg_hnd_cnt = 0;
166     pj_bzero(err_msg_hnd, sizeof(err_msg_hnd));
167 }
168 
169 
170 /*
171  * pj_strerror()
172  */
pj_strerror(pj_status_t statcode,char * buf,pj_size_t bufsize)173 PJ_DEF(pj_str_t) pj_strerror( pj_status_t statcode,
174 			      char *buf, pj_size_t bufsize )
175 {
176     int len = -1;
177     pj_str_t errstr;
178 
179     pj_assert(buf && bufsize);
180 
181     if (statcode == PJ_SUCCESS) {
182 	len = pj_ansi_snprintf( buf, bufsize, "Success");
183 
184     } else if (statcode < PJ_ERRNO_START + PJ_ERRNO_SPACE_SIZE) {
185         len = pj_ansi_snprintf( buf, bufsize, "Unknown error %d", statcode);
186 
187     } else if (statcode < PJ_ERRNO_START_STATUS + PJ_ERRNO_SPACE_SIZE) {
188         len = pjlib_error(statcode, buf, bufsize);
189 
190     } else if (statcode < PJ_ERRNO_START_SYS + PJ_ERRNO_SPACE_SIZE) {
191         len = platform_strerror(PJ_STATUS_TO_OS(statcode), buf, bufsize);
192 
193     } else {
194 	unsigned i;
195 
196 	/* Find user handler to get the error message. */
197 	for (i=0; i<err_msg_hnd_cnt; ++i) {
198 	    if (IN_RANGE(statcode, err_msg_hnd[i].begin, err_msg_hnd[i].end)) {
199 		return (*err_msg_hnd[i].strerror)(statcode, buf, bufsize);
200 	    }
201 	}
202 
203 	/* Handler not found! */
204 	len = pj_ansi_snprintf( buf, bufsize, "Unknown error %d", statcode);
205     }
206 
207     if (len < 1 || len >= (int)bufsize) {
208 	len = (int)(bufsize - 1);
209 	buf[len] = '\0';
210     }
211 
212     errstr.ptr = buf;
213     errstr.slen = len;
214 
215     return errstr;
216 }
217 
218 #if PJ_LOG_MAX_LEVEL >= 1
invoke_log(const char * sender,int level,const char * format,...)219 static void invoke_log(const char *sender, int level, const char *format, ...)
220 {
221     va_list arg;
222     va_start(arg, format);
223     pj_log(sender, level, format, arg);
224     va_end(arg);
225 }
226 
pj_perror_imp(int log_level,const char * sender,pj_status_t status,const char * title_fmt,va_list marker)227 static void pj_perror_imp(int log_level, const char *sender,
228 			  pj_status_t status,
229 		          const char *title_fmt, va_list marker)
230 {
231     char titlebuf[PJ_PERROR_TITLE_BUF_SIZE];
232     char errmsg[PJ_ERR_MSG_SIZE];
233     int len;
234 
235     /* Build the title */
236     len = pj_ansi_vsnprintf(titlebuf, sizeof(titlebuf), title_fmt, marker);
237     if (len < 0 || len >= (int)sizeof(titlebuf))
238 	pj_ansi_strcpy(titlebuf, "Error");
239 
240     /* Get the error */
241     pj_strerror(status, errmsg, sizeof(errmsg));
242 
243     /* Send to log */
244     invoke_log(sender, log_level, "%s: %s", titlebuf, errmsg);
245 }
246 
pj_perror(int log_level,const char * sender,pj_status_t status,const char * title_fmt,...)247 PJ_DEF(void) pj_perror(int log_level, const char *sender, pj_status_t status,
248 		       const char *title_fmt, ...)
249 {
250     va_list marker;
251     va_start(marker, title_fmt);
252     pj_perror_imp(log_level, sender, status, title_fmt, marker);
253     va_end(marker);
254 }
255 
pj_perror_1(const char * sender,pj_status_t status,const char * title_fmt,...)256 PJ_DEF(void) pj_perror_1(const char *sender, pj_status_t status,
257 			 const char *title_fmt, ...)
258 {
259     va_list marker;
260     va_start(marker, title_fmt);
261     pj_perror_imp(1, sender, status, title_fmt, marker);
262     va_end(marker);
263 }
264 
265 #else /* #if PJ_LOG_MAX_LEVEL >= 1 */
pj_perror(int log_level,const char * sender,pj_status_t status,const char * title_fmt,...)266 PJ_DEF(void) pj_perror(int log_level, const char *sender, pj_status_t status,
267 		       const char *title_fmt, ...)
268 {
269 }
270 #endif	/* #if PJ_LOG_MAX_LEVEL >= 1 */
271 
272 
273 #if PJ_LOG_MAX_LEVEL >= 2
pj_perror_2(const char * sender,pj_status_t status,const char * title_fmt,...)274 PJ_DEF(void) pj_perror_2(const char *sender, pj_status_t status,
275 			 const char *title_fmt, ...)
276 {
277     va_list marker;
278     va_start(marker, title_fmt);
279     pj_perror_imp(2, sender, status, title_fmt, marker);
280     va_end(marker);
281 }
282 #endif
283 
284 #if PJ_LOG_MAX_LEVEL >= 3
pj_perror_3(const char * sender,pj_status_t status,const char * title_fmt,...)285 PJ_DEF(void) pj_perror_3(const char *sender, pj_status_t status,
286 			 const char *title_fmt, ...)
287 {
288     va_list marker;
289     va_start(marker, title_fmt);
290     pj_perror_imp(3, sender, status, title_fmt, marker);
291     va_end(marker);
292 }
293 #endif
294 
295 #if PJ_LOG_MAX_LEVEL >= 4
pj_perror_4(const char * sender,pj_status_t status,const char * title_fmt,...)296 PJ_DEF(void) pj_perror_4(const char *sender, pj_status_t status,
297 			 const char *title_fmt, ...)
298 {
299     va_list marker;
300     va_start(marker, title_fmt);
301     pj_perror_imp(4, sender, status, title_fmt, marker);
302     va_end(marker);
303 }
304 #endif
305 
306 #if PJ_LOG_MAX_LEVEL >= 5
pj_perror_5(const char * sender,pj_status_t status,const char * title_fmt,...)307 PJ_DEF(void) pj_perror_5(const char *sender, pj_status_t status,
308 			 const char *title_fmt, ...)
309 {
310     va_list marker;
311     va_start(marker, title_fmt);
312     pj_perror_imp(5, sender, status, title_fmt, marker);
313     va_end(marker);
314 }
315 #endif
316 
317 #if PJ_LOG_MAX_LEVEL >= 6
pj_perror_6(const char * sender,pj_status_t status,const char * title_fmt,...)318 PJ_DEF(void) pj_perror_6(const char *sender, pj_status_t status,
319 			 const char *title_fmt, ...)
320 {
321     va_list marker;
322     va_start(marker, title_fmt);
323     pj_perror_imp(6, sender, status, title_fmt, marker);
324     va_end(marker);
325 }
326 #endif
327 
328