1 /*
2  * Copyright (C) 2009 Sippy Software, Inc., http://www.sippysoft.com
3  *
4  * This file is part of Kamailio, a free SIP server.
5  *
6  * Kamailio 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  * Kamailio 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21 
22 #include <Python.h>
23 #include <stdio.h>
24 #include <stdarg.h>
25 
26 #include "../../core/dprint.h"
27 #include "../../core/mem/mem.h"
28 
29 #include "app_python_mod.h"
30 #include "python_support.h"
31 
32 static char *make_message(const char *fmt, va_list ap);
33 
python_handle_exception(const char * fmt,...)34 void python_handle_exception(const char *fmt, ...)
35 {
36 	PyObject *pResult;
37 	const char *msg;
38 	char *buf;
39 	size_t buflen, msglen;
40 	PyObject *exception, *v, *tb, *args;
41 	PyObject *line;
42 	int i;
43 	char *srcbuf;
44 	int exc_exit = 0;
45 	va_list ap;
46 
47 	// We don't want to generate traceback when no errors occurred
48 	if (!PyErr_Occurred())
49 		return;
50 
51 	if (fmt == NULL)
52 		srcbuf = NULL;
53 	else {
54 		va_start(ap, fmt);
55 		srcbuf = make_message(fmt, ap);
56 		va_end(ap);
57 	}
58 
59 	PyErr_Fetch(&exception, &v, &tb);
60 	PyErr_Clear();
61 	if (exception == NULL) {
62 		LM_ERR("Can't get traceback, PyErr_Fetch() has failed.\n");
63 		if (srcbuf) pkg_free(srcbuf);
64 		return;
65 	}
66 
67 	PyErr_NormalizeException(&exception, &v, &tb);
68 	if (exception == NULL) {
69 		LM_ERR("Can't get traceback, PyErr_NormalizeException() has failed.\n");
70 		if (srcbuf) pkg_free(srcbuf);
71 		return;
72 	}
73 
74 	exc_exit = PyErr_GivenExceptionMatches(exception, PyExc_SystemExit);
75 	args = PyTuple_Pack(3, exception, v, tb ? tb : Py_None);
76 	Py_XDECREF(exception);
77 	Py_XDECREF(v);
78 	Py_XDECREF(tb);
79 	if (args == NULL) {
80 		LM_ERR("Can't get traceback, PyTuple_Pack() has failed.\n");
81 		if (srcbuf) pkg_free(srcbuf);
82 		return;
83 	}
84 
85 	pResult = PyObject_CallObject(format_exc_obj, args);
86 	Py_DECREF(args);
87 	if (pResult == NULL) {
88 		LM_ERR("Can't get traceback, traceback.format_exception() has failed.\n");
89 		if (srcbuf) pkg_free(srcbuf);
90 		return;
91 	}
92 
93 	buflen = 1;
94 	buf = (char *)pkg_realloc(NULL, buflen * sizeof(char));
95 	if (!buf)
96 	{
97 		PKG_MEM_ERROR;
98 		if (srcbuf) pkg_free(srcbuf);
99 		return;
100 	}
101 	memset(buf, 0, buflen * sizeof(char));
102 
103 	for (i = 0; i < PySequence_Size(pResult); i++) {
104 		line = PySequence_GetItem(pResult, i);
105 		if (line == NULL) {
106 			LM_ERR("Can't get traceback, PySequence_GetItem() has failed.\n");
107 			Py_DECREF(pResult);
108 			if (buf)
109 				pkg_free(buf);
110 			if (srcbuf) pkg_free(srcbuf);
111 			return;
112 		}
113 
114 		msg = PyString_AsString(line);
115 
116 		if (msg == NULL) {
117 			LM_ERR("Can't get traceback, PyString_AsString() has failed.\n");
118 			Py_DECREF(line);
119 			Py_DECREF(pResult);
120 			if (buf)
121 				pkg_free(buf);
122 			if (srcbuf) pkg_free(srcbuf);
123 			return;
124 		}
125 
126 		msglen = strlen(msg);
127 		buflen += ++msglen;
128 
129 		buf = (char *)pkg_reallocxf(buf, buflen * sizeof(char));
130 		if (!buf)
131 		{
132 			PKG_MEM_ERROR;
133 			Py_DECREF(line);
134 			Py_DECREF(pResult);
135 			if (srcbuf) pkg_free(srcbuf);
136 			return;
137 		}
138 
139 		strncat(buf, msg, msglen >= buflen ? buflen-1 : msglen);
140 
141 		Py_DECREF(line);
142 	}
143 
144 	if (srcbuf == NULL) {
145 		if(exc_exit) {
146 			LM_DBG("Unhandled exception in the Python code:\n%s", buf);
147 		} else {
148 			LM_ERR("Unhandled exception in the Python code:\n%s", buf);
149 		}
150 	} else {
151 		if(exc_exit) {
152 			LM_DBG("%s: Unhandled exception in the Python code:\n%s", srcbuf, buf);
153 		} else {
154 			LM_ERR("%s: Unhandled exception in the Python code:\n%s", srcbuf, buf);
155 		}
156 	}
157 
158 	if (buf)
159 		pkg_free(buf);
160 
161 	if (srcbuf)
162 		pkg_free(srcbuf);
163 
164 	Py_DECREF(pResult);
165 }
166 
167 
InitTracebackModule()168 PyObject *InitTracebackModule()
169 {
170 	PyObject *pModule, *pTracebackObject;
171 
172 	pModule = PyImport_ImportModule("traceback");
173 	if (pModule == NULL) {
174 		LM_ERR("Cannot import module 'traceback'.\n");
175 		return NULL;
176 	}
177 
178 	pTracebackObject = PyObject_GetAttrString(pModule, "format_exception");
179 	Py_DECREF(pModule);
180 	if (pTracebackObject == NULL || !PyCallable_Check(pTracebackObject)) {
181 		LM_ERR("AttributeError: 'module' object 'traceback' has no attribute"
182 				" 'format_exception'.\n");
183 		Py_XDECREF(pTracebackObject);
184 		return NULL;
185 	}
186 
187 	return pTracebackObject;
188 }
189 
190 
make_message(const char * fmt,va_list ap)191 static char *make_message(const char *fmt, va_list ap)
192 {
193 	int n;
194 	size_t size;
195 	char *p, *np;
196 
197 	size = 100;     /* Guess we need no more than 100 bytes. */
198 	p = (char *)pkg_realloc(NULL, size * sizeof(char));
199 	if (!p)
200 	{
201 		PKG_MEM_ERROR;
202 		return NULL;
203 	}
204 	memset(p, 0, size * sizeof(char));
205 
206 	while (1)
207 	{
208 		n = vsnprintf(p, size, fmt, ap);
209 
210 		if (n > -1 && n < size)
211 			return p;
212 
213 		if (n > -1)    /* glibc 2.1 */
214 			size = n+1;
215 		else           /* glibc 2.0 */
216 			size *= 2;
217 
218 		np = (char *)pkg_realloc(p, size * sizeof(char));
219 		if (!np)
220 		{
221 			PKG_MEM_ERROR;
222 			if (p)
223 				pkg_free(p);
224 			return NULL;
225 		}
226 		else
227 			p = np;
228 	}
229 
230 	return NULL;	// shall not happened, but who knows ;)
231 }
232 
get_class_name(PyObject * y)233 char *get_class_name(PyObject *y)
234 {
235 	PyObject *p;
236 	char *name;
237 
238 	p = PyObject_GetAttrString(y, "__name__");
239 	if (p == NULL || p == Py_None)
240 	{
241 		Py_XDECREF(p);
242 		return NULL;
243 	}
244 
245 	name = PyString_AsString(p);
246 	Py_XDECREF(p);
247 
248 	return name;
249 }
250 
251 
get_instance_class_name(PyObject * y)252 char *get_instance_class_name(PyObject *y)
253 {
254 	PyObject *p, *n;
255 	char *name;
256 
257 	n = PyObject_GetAttrString(y, "__class__");
258 	if (n == NULL || n == Py_None)
259 	{
260 		Py_XDECREF(n);
261 		return NULL;
262 	}
263 
264 	p = PyObject_GetAttrString(n, "__name__");
265 	if (p == NULL || p == Py_None)
266 	{
267 		Py_XDECREF(p);
268 		return NULL;
269 	}
270 
271 	name = PyString_AsString(p);
272 	Py_XDECREF(p);
273 	Py_XDECREF(n);
274 
275 	return name;
276 }
277