]> granicus.if.org Git - icinga2/blob - lib/base/exception.cpp
Merge pull request #7185 from Icinga/bugfix/gelfwriter-wrong-log-facility
[icinga2] / lib / base / exception.cpp
1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
3 #include "base/exception.hpp"
4 #include <boost/thread/tss.hpp>
5
6 #ifdef HAVE_CXXABI_H
7 #       include <cxxabi.h>
8 #endif /* HAVE_CXXABI_H */
9
10 using namespace icinga;
11
12 static boost::thread_specific_ptr<StackTrace> l_LastExceptionStack;
13 static boost::thread_specific_ptr<ContextTrace> l_LastExceptionContext;
14
15 #ifdef HAVE_CXXABI_H
16
17 #ifdef _LIBCPPABI_VERSION
18 class libcxx_type_info : public std::type_info
19 {
20 public:
21         ~libcxx_type_info() override;
22
23         virtual void noop1() const;
24         virtual void noop2() const;
25         virtual bool can_catch(const libcxx_type_info *thrown_type, void *&adjustedPtr) const = 0;
26 };
27 #endif /* _LIBCPPABI_VERSION */
28
29
30 #if defined(__GLIBCXX__) || defined(_LIBCPPABI_VERSION)
31 inline void *cast_exception(void *obj, const std::type_info *src, const std::type_info *dst)
32 {
33 #ifdef __GLIBCXX__
34         void *thrown_ptr = obj;
35
36         /* Check if the exception is a pointer type. */
37         if (src->__is_pointer_p())
38                 thrown_ptr = *(void **)thrown_ptr;
39
40         if (dst->__do_catch(src, &thrown_ptr, 1))
41                 return thrown_ptr;
42         else
43                 return nullptr;
44 #else /* __GLIBCXX__ */
45         const auto *srcInfo = static_cast<const libcxx_type_info *>(src);
46         const auto *dstInfo = static_cast<const libcxx_type_info *>(dst);
47
48         void *adj = obj;
49
50         if (dstInfo->can_catch(srcInfo, adj))
51                 return adj;
52         else
53                 return nullptr;
54 #endif /* __GLIBCXX__ */
55
56 }
57 #else /* defined(__GLIBCXX__) || defined(_LIBCPPABI_VERSION) */
58 #define NO_CAST_EXCEPTION
59 #endif /* defined(__GLIBCXX__) || defined(_LIBCPPABI_VERSION) */
60
61 #       if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ > 3)
62 #               define TYPEINFO_TYPE std::type_info
63 #       else
64 #               define TYPEINFO_TYPE void
65 #       endif
66
67 #       if !defined(__GLIBCXX__) && !defined(_WIN32)
68 static boost::thread_specific_ptr<void *> l_LastExceptionObj;
69 static boost::thread_specific_ptr<TYPEINFO_TYPE *> l_LastExceptionPvtInfo;
70
71 typedef void (*DestCallback)(void *);
72 static boost::thread_specific_ptr<DestCallback> l_LastExceptionDest;
73 #       endif /* !__GLIBCXX__ && !_WIN32 */
74
75 extern "C" void __cxa_throw(void *obj, TYPEINFO_TYPE *pvtinfo, void (*dest)(void *));
76 #endif /* HAVE_CXXABI_H */
77
78 void icinga::RethrowUncaughtException()
79 {
80 #if defined(__GLIBCXX__) || !defined(HAVE_CXXABI_H)
81         throw;
82 #else /* __GLIBCXX__ || !HAVE_CXXABI_H */
83         __cxa_throw(*l_LastExceptionObj.get(), *l_LastExceptionPvtInfo.get(), *l_LastExceptionDest.get());
84 #endif /* __GLIBCXX__ || !HAVE_CXXABI_H */
85 }
86
87 #ifdef HAVE_CXXABI_H
88 extern "C"
89 void __cxa_throw(void *obj, TYPEINFO_TYPE *pvtinfo, void (*dest)(void *))
90 {
91         auto *tinfo = static_cast<std::type_info *>(pvtinfo);
92
93         typedef void (*cxa_throw_fn)(void *, std::type_info *, void (*)(void *)) __attribute__((noreturn));
94         static cxa_throw_fn real_cxa_throw;
95
96 #if !defined(__GLIBCXX__) && !defined(_WIN32)
97         l_LastExceptionObj.reset(new void *(obj));
98         l_LastExceptionPvtInfo.reset(new TYPEINFO_TYPE *(pvtinfo));
99         l_LastExceptionDest.reset(new DestCallback(dest));
100 #endif /* !defined(__GLIBCXX__) && !defined(_WIN32) */
101
102         if (real_cxa_throw == nullptr)
103                 real_cxa_throw = (cxa_throw_fn)dlsym(RTLD_NEXT, "__cxa_throw");
104
105 #ifndef NO_CAST_EXCEPTION
106         void *uex = cast_exception(obj, tinfo, &typeid(user_error));
107         auto *ex = reinterpret_cast<boost::exception *>(cast_exception(obj, tinfo, &typeid(boost::exception)));
108
109         if (!uex) {
110 #endif /* NO_CAST_EXCEPTION */
111                 StackTrace stack;
112                 SetLastExceptionStack(stack);
113
114 #ifndef NO_CAST_EXCEPTION
115                 if (ex && !boost::get_error_info<StackTraceErrorInfo>(*ex))
116                         *ex << StackTraceErrorInfo(stack);
117         }
118 #endif /* NO_CAST_EXCEPTION */
119
120         ContextTrace context;
121         SetLastExceptionContext(context);
122
123 #ifndef NO_CAST_EXCEPTION
124         if (ex && !boost::get_error_info<ContextTraceErrorInfo>(*ex))
125                 *ex << ContextTraceErrorInfo(context);
126 #endif /* NO_CAST_EXCEPTION */
127
128         real_cxa_throw(obj, tinfo, dest);
129 }
130 #endif /* HAVE_CXXABI_H */
131
132 StackTrace *icinga::GetLastExceptionStack()
133 {
134         return l_LastExceptionStack.get();
135 }
136
137 void icinga::SetLastExceptionStack(const StackTrace& trace)
138 {
139         l_LastExceptionStack.reset(new StackTrace(trace));
140 }
141
142 ContextTrace *icinga::GetLastExceptionContext()
143 {
144         return l_LastExceptionContext.get();
145 }
146
147 void icinga::SetLastExceptionContext(const ContextTrace& context)
148 {
149         l_LastExceptionContext.reset(new ContextTrace(context));
150 }
151
152 String icinga::DiagnosticInformation(const std::exception& ex, bool verbose, StackTrace *stack, ContextTrace *context)
153 {
154         std::ostringstream result;
155
156         String message = ex.what();
157
158         const auto *vex = dynamic_cast<const ValidationError *>(&ex);
159
160         if (message.IsEmpty())
161                 result << boost::diagnostic_information(ex) << "\n";
162         else
163                 result << "Error: " << message << "\n";
164
165         const auto *dex = dynamic_cast<const ScriptError *>(&ex);
166
167         if (dex && !dex->GetDebugInfo().Path.IsEmpty())
168                 ShowCodeLocation(result, dex->GetDebugInfo());
169
170         if (vex) {
171                 DebugInfo di;
172
173                 ConfigObject::Ptr dobj = vex->GetObject();
174                 if (dobj)
175                         di = dobj->GetDebugInfo();
176
177                 Dictionary::Ptr currentHint = vex->GetDebugHint();
178                 Array::Ptr messages;
179
180                 if (currentHint) {
181                         for (const String& attr : vex->GetAttributePath()) {
182                                 Dictionary::Ptr props = currentHint->Get("properties");
183
184                                 if (!props)
185                                         break;
186
187                                 currentHint = props->Get(attr);
188
189                                 if (!currentHint)
190                                         break;
191
192                                 messages = currentHint->Get("messages");
193                         }
194                 }
195
196                 if (messages && messages->GetLength() > 0) {
197                         Array::Ptr message = messages->Get(messages->GetLength() - 1);
198
199                         di.Path = message->Get(1);
200                         di.FirstLine = message->Get(2);
201                         di.FirstColumn = message->Get(3);
202                         di.LastLine = message->Get(4);
203                         di.LastColumn = message->Get(5);
204                 }
205
206                 if (!di.Path.IsEmpty())
207                         ShowCodeLocation(result, di);
208         }
209
210         const auto *uex = dynamic_cast<const user_error *>(&ex);
211         const auto *pex = dynamic_cast<const posix_error *>(&ex);
212
213         if (!uex && !pex && verbose) {
214                 const StackTrace *st = boost::get_error_info<StackTraceErrorInfo>(ex);
215
216                 if (st) {
217                         result << *st;
218                 } else {
219                         result << std::endl;
220
221                         if (!stack)
222                                 stack = GetLastExceptionStack();
223
224                         if (stack)
225                                 result << *stack;
226
227                 }
228         }
229
230         const ContextTrace *ct = boost::get_error_info<ContextTraceErrorInfo>(ex);
231
232         if (ct) {
233                 result << *ct;
234         } else {
235                 result << std::endl;
236
237                 if (!context)
238                         context = GetLastExceptionContext();
239
240                 if (context)
241                         result << *context;
242         }
243
244         return result.str();
245 }
246
247 String icinga::DiagnosticInformation(const boost::exception_ptr& eptr, bool verbose)
248 {
249         StackTrace *pt = GetLastExceptionStack();
250         StackTrace stack;
251
252         ContextTrace *pc = GetLastExceptionContext();
253         ContextTrace context;
254
255         if (pt)
256                 stack = *pt;
257
258         if (pc)
259                 context = *pc;
260
261         try {
262                 boost::rethrow_exception(eptr);
263         } catch (const std::exception& ex) {
264                 return DiagnosticInformation(ex, verbose, pt ? &stack : nullptr, pc ? &context : nullptr);
265         }
266
267         return boost::diagnostic_information(eptr);
268 }
269
270 ScriptError::ScriptError(String message)
271         : m_Message(std::move(message)), m_IncompleteExpr(false)
272 { }
273
274 ScriptError::ScriptError(String message, DebugInfo di, bool incompleteExpr)
275         : m_Message(std::move(message)), m_DebugInfo(std::move(di)), m_IncompleteExpr(incompleteExpr), m_HandledByDebugger(false)
276 { }
277
278 const char *ScriptError::what() const throw()
279 {
280         return m_Message.CStr();
281 }
282
283 DebugInfo ScriptError::GetDebugInfo() const
284 {
285         return m_DebugInfo;
286 }
287
288 bool ScriptError::IsIncompleteExpression() const
289 {
290         return m_IncompleteExpr;
291 }
292
293 bool ScriptError::IsHandledByDebugger() const
294 {
295         return m_HandledByDebugger;
296 }
297
298 void ScriptError::SetHandledByDebugger(bool handled)
299 {
300         m_HandledByDebugger = handled;
301 }
302
303 posix_error::~posix_error() throw()
304 {
305         free(m_Message);
306 }
307
308 const char *posix_error::what() const throw()
309 {
310         if (!m_Message) {
311                 std::ostringstream msgbuf;
312
313                 const char * const *func = boost::get_error_info<boost::errinfo_api_function>(*this);
314
315                 if (func)
316                         msgbuf << "Function call '" << *func << "'";
317                 else
318                         msgbuf << "Function call";
319
320                 const std::string *fname = boost::get_error_info<boost::errinfo_file_name>(*this);
321
322                 if (fname)
323                         msgbuf << " for file '" << *fname << "'";
324
325                 msgbuf << " failed";
326
327                 const int *errnum = boost::get_error_info<boost::errinfo_errno>(*this);
328
329                 if (errnum)
330                         msgbuf << " with error code " << *errnum << ", '" << strerror(*errnum) << "'";
331
332                 String str = msgbuf.str();
333                 m_Message = strdup(str.CStr());
334         }
335
336         return m_Message;
337 }
338
339 ValidationError::ValidationError(const ConfigObject::Ptr& object, const std::vector<String>& attributePath, const String& message)
340         : m_Object(object), m_AttributePath(attributePath), m_Message(message)
341 {
342         String path;
343
344         for (const String& attribute : attributePath) {
345                 if (!path.IsEmpty())
346                         path += " -> ";
347
348                 path += "'" + attribute + "'";
349         }
350
351         Type::Ptr type = object->GetReflectionType();
352         m_What = "Validation failed for object '" + object->GetName() + "' of type '" + type->GetName() + "'";
353
354         if (!path.IsEmpty())
355                 m_What += "; Attribute " + path;
356
357         m_What += ": " + message;
358 }
359
360 ValidationError::~ValidationError() throw()
361 { }
362
363 const char *ValidationError::what() const throw()
364 {
365         return m_What.CStr();
366 }
367
368 ConfigObject::Ptr ValidationError::GetObject() const
369 {
370         return m_Object;
371 }
372
373 std::vector<String> ValidationError::GetAttributePath() const
374 {
375         return m_AttributePath;
376 }
377
378 String ValidationError::GetMessage() const
379 {
380         return m_Message;
381 }
382
383 void ValidationError::SetDebugHint(const Dictionary::Ptr& dhint)
384 {
385         m_DebugHint = dhint;
386 }
387
388 Dictionary::Ptr ValidationError::GetDebugHint() const
389 {
390         return m_DebugHint;
391 }
392
393 std::string icinga::to_string(const StackTraceErrorInfo&)
394 {
395         return "";
396 }
397
398 #ifdef _WIN32
399 std::string icinga::to_string(const errinfo_win32_error& e)
400 {
401         return "[errinfo_win32_error] = " + Utility::FormatErrorNumber(e.value()) + "\n";
402 }
403 #endif /* _WIN32 */
404
405 std::string icinga::to_string(const errinfo_getaddrinfo_error& e)
406 {
407         String msg;
408
409 #ifdef _WIN32
410         msg = gai_strerrorA(e.value());
411 #else /* _WIN32 */
412         msg = gai_strerror(e.value());
413 #endif /* _WIN32 */
414
415         return "[errinfo_getaddrinfo_error] = " + String(msg) + "\n";
416 }
417
418 std::string icinga::to_string(const ContextTraceErrorInfo& e)
419 {
420         std::ostringstream msgbuf;
421         msgbuf << "[Context] = " << e.value();
422         return msgbuf.str();
423 }