1 /*
2 * Copyright 2017 Jan Pokorny <jpokorny@redhat.com>
3 *
4 * This program 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 of the License, or (at your option) any later version.
8 *
9 * This software 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 * 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; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18 #ifndef CRM_COMMON_XML_INTERNAL__H
19 # define CRM_COMMON_XML_INTERNAL__H
20
21 /*
22 * Internal-only wrappers for and extensions to libxml2 (libxslt)
23 */
24
25 # include <stdlib.h>
26 # include <stdio.h>
27 # include <string.h>
28
29 # include <crm/crm.h> /* transitively imports qblog.h */
30
31
32 /*!
33 * \brief Base for directing lib{xml2,xslt} log into standard libqb backend
34 *
35 * This macro implements the core of what can be needed for directing
36 * libxml2 or libxslt error messaging into standard, preconfigured
37 * libqb-backed log stream.
38 *
39 * It's a bit unfortunate that libxml2 (and more sparsely, also libxslt)
40 * emits a single message by chunks (location is emitted separatedly from
41 * the message itself), so we have to take the effort to combine these
42 * chunks back to single message. Whether to do this or not is driven
43 * with \p dechunk toggle.
44 *
45 * The form of a macro was chosen for implicit deriving of __FILE__, etc.
46 * and also because static dechunking buffer should be differentiated per
47 * library (here we assume different functions referring to this macro
48 * will not ever be using both at once), preferably also per-library
49 * context of use to avoid clashes altogether.
50 *
51 * Note that we cannot use qb_logt, because callsite data have to be known
52 * at the moment of compilation, which it is not always the case -- xml_log
53 * (and unfortunately there's no clear explanation of the fail to compile).
54 *
55 * Also note that there's no explicit guard against said libraries producing
56 * never-newline-terminated chunks (which would just keep consuming memory),
57 * as it's quite improbable. Termination of the program in between the
58 * same-message chunks will raise a flag with valgrind and the likes, though.
59 *
60 * \param[in] priority Syslog priority for the message to be logged
61 * \param[in] dechunk Whether to dechunk new-line terminated message
62 * \param[in] postemit Code to be executed once message is sent out
63 * \param[in] prefix How to prefix the message or NULL for raw passing
64 * \param[in] fmt Format string as with printf-like functions
65 * \param[in] ap Variable argument list to supplement \p fmt format string
66 */
67 #define CRM_XML_LOG_BASE(priority, dechunk, postemit, prefix, fmt, ap) \
68 do { \
69 if (!(dechunk) && (prefix) == NULL) { /* quick pass */ \
70 qb_log_from_external_source_va(__FUNCTION__, __FILE__, (fmt), \
71 (priority), __LINE__, 0, (ap)); \
72 (void) (postemit); \
73 } else { \
74 int CXLB_len = 0; \
75 char *CXLB_buf = NULL; \
76 static int CXLB_buffer_len = 0; \
77 static char *CXLB_buffer = NULL; \
78 \
79 CXLB_len = vasprintf(&CXLB_buf, (fmt), (ap)); \
80 \
81 if (CXLB_len <= 0 || CXLB_buf[CXLB_len - 1] == '\n' || !(dechunk)) { \
82 if (CXLB_len < 0) { \
83 CXLB_buf = (char *) "LOG CORRUPTION HAZARD"; /*we don't modify*/\
84 } else if (CXLB_len > 0 /* && (dechunk) */ \
85 && CXLB_buf[CXLB_len - 1] == '\n') { \
86 CXLB_buf[CXLB_len - 1] = '\0'; \
87 } \
88 if (CXLB_buffer) { \
89 qb_log_from_external_source(__FUNCTION__, __FILE__, "%s%s%s", \
90 (priority), __LINE__, 0, \
91 (prefix) != NULL ? (prefix) : "", \
92 CXLB_buffer, CXLB_buf); \
93 free(CXLB_buffer); \
94 } else { \
95 qb_log_from_external_source(__FUNCTION__, __FILE__, "%s%s", \
96 (priority), __LINE__, 0, \
97 (prefix) != NULL ? (prefix) : "", \
98 CXLB_buf); \
99 } \
100 if (CXLB_len < 0) { \
101 CXLB_buf = NULL; /* restore temporary override */ \
102 } \
103 CXLB_buffer = NULL; \
104 CXLB_buffer_len = 0; \
105 (void) (postemit); \
106 \
107 } else if (CXLB_buffer == NULL) { \
108 CXLB_buffer_len = CXLB_len; \
109 CXLB_buffer = CXLB_buf; \
110 CXLB_buf = NULL; \
111 \
112 } else { \
113 CXLB_buffer = realloc(CXLB_buffer, 1 + CXLB_buffer_len + CXLB_len); \
114 memcpy(CXLB_buffer + CXLB_buffer_len, CXLB_buf, CXLB_len); \
115 CXLB_buffer_len += CXLB_len; \
116 CXLB_buffer[CXLB_buffer_len] = '\0'; \
117 } \
118 free(CXLB_buf); \
119 } \
120 } while (0)
121
122 #endif