1 /* The errmsg object.
2  *
3  * Module begun 2008-03-05 by Rainer Gerhards, based on some code
4  * from syslogd.c. I converted this module to lgpl and have checked that
5  * all contributors agreed to that step.
6  * Now moving to ASL 2.0, and contributor checks tell that there is no need
7  * to take further case, as the code now boils to be either my own or, a few lines,
8  * of the original BSD-licenses sysklogd code. rgerhards, 2012-01-16
9  *
10  * Copyright 2008-2018 Adiscon GmbH.
11  *
12  * This file is part of the rsyslog runtime library.
13  *
14  * Licensed under the Apache License, Version 2.0 (the "License");
15  * you may not use this file except in compliance with the License.
16  * You may obtain a copy of the License at
17  *
18  *       http://www.apache.org/licenses/LICENSE-2.0
19  *       -or-
20  *       see COPYING.ASL20 in the source distribution
21  *
22  * Unless required by applicable law or agreed to in writing, software
23  * distributed under the License is distributed on an "AS IS" BASIS,
24  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25  * See the License for the specific language governing permissions and
26  * limitations under the License.
27  */
28 
29 #include "config.h"
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <errno.h>
34 #include <assert.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 #include <string.h>
39 #include <unistd.h>
40 
41 #include "rsyslog.h"
42 #include "obj.h"
43 #include "msg.h"
44 #include "errmsg.h"
45 #include "operatingstate.h"
46 #include "srUtils.h"
47 #include "stringbuf.h"
48 
49 /* static data */
50 #ifndef O_LARGEFILE
51 #define O_LARGEFILE 0
52 #endif
53 
54 static int bHadErrMsgs; /* indicates if we had error messages since reset of this flag
55 			 * This is used to abort a run if the config is unclean.
56 			 */
57 
58 static int fdOversizeMsgLog = -1;
59 static pthread_mutex_t oversizeMsgLogMut = PTHREAD_MUTEX_INITIALIZER;
60 
61 /* ------------------------------ methods ------------------------------ */
62 
63 /* Resets the error message flag. Must be done before processing config
64  * files.
65  */
66 void
resetErrMsgsFlag(void)67 resetErrMsgsFlag(void)
68 {
69 	bHadErrMsgs = 0;
70 }
71 
72 int
hadErrMsgs(void)73 hadErrMsgs(void)
74 {
75 	return bHadErrMsgs;
76 }
77 
78 /* We now receive three parameters: one is the internal error code
79  * which will also become the error message number, the second is
80  * errno - if it is non-zero, the corresponding error message is included
81  * in the text and finally the message text itself. Note that it is not
82  * 100% clean to use the internal errcode, as it may be reached from
83  * multiple actual error causes. However, it is much better than having
84  * no error code at all (and in most cases, a single internal error code
85  * maps to a specific error event).
86  * rgerhards, 2008-06-27
87  */
88 static void
doLogMsg(const int iErrno,const int iErrCode,const int severity,const char * msg)89 doLogMsg(const int iErrno, const int iErrCode,  const int severity, const char *msg)
90 {
91 	char buf[2048];
92 	char errStr[1024];
93 
94 	dbgprintf("Called LogMsg, msg: %s\n", msg);
95 	osf_write(OSF_TAG_MSG, msg);
96 
97 	if(iErrno != 0) {
98 		rs_strerror_r(iErrno, errStr, sizeof(errStr));
99 		if(iErrCode == NO_ERRCODE || iErrCode == RS_RET_ERR) {
100 			snprintf(buf, sizeof(buf), "%s: %s [v%s]", msg, errStr, VERSION);
101 		} else {
102 			snprintf(buf, sizeof(buf), "%s: %s [v%s try https://www.rsyslog.com/e/%d ]",
103 				msg, errStr, VERSION, iErrCode * -1);
104 		}
105 	} else {
106 		if(iErrCode == NO_ERRCODE || iErrCode == RS_RET_ERR) {
107 			snprintf(buf, sizeof(buf), "%s [v%s]", msg, VERSION);
108 		} else {
109 			snprintf(buf, sizeof(buf), "%s [v%s try https://www.rsyslog.com/e/%d ]", msg,
110 				VERSION, iErrCode * -1);
111 		}
112 	}
113 	buf[sizeof(buf) - 1] = '\0'; /* just to be on the safe side... */
114 	errno = 0;
115 
116 	const int msglen = (int) strlen(buf);
117 	if(msglen > glblGetMaxLine()) {
118 		/* in extreme cases, our error messages may be longer than the configured
119 		 * max message size. If so, we just truncate without further indication, as
120 		 * anything else would probably lead to a death loop on error messages.
121 		 * Note that we do not split, as we really do not anticipate there is
122 		 * much value in supporting extremely short max message sizes - we assume
123 		 * it's just a testbench thing. -- rgerhards, 2018-05-11
124 		 */
125 		 buf[glblGetMaxLine()] = '\0'; /* space must be available! */
126 	}
127 
128 	glblErrLogger(severity, iErrCode, (uchar*)buf);
129 
130 	if(severity == LOG_ERR)
131 		bHadErrMsgs = 1;
132 }
133 
134 /* We now receive three parameters: one is the internal error code
135  * which will also become the error message number, the second is
136  * errno - if it is non-zero, the corresponding error message is included
137  * in the text and finally the message text itself. Note that it is not
138  * 100% clean to use the internal errcode, as it may be reached from
139  * multiple actual error causes. However, it is much better than having
140  * no error code at all (and in most cases, a single internal error code
141  * maps to a specific error event).
142  * rgerhards, 2008-06-27
143  */
144 void __attribute__((format(printf, 3, 4)))
LogError(const int iErrno,const int iErrCode,const char * fmt,...)145 LogError(const int iErrno, const int iErrCode, const char *fmt, ... )
146 {
147 	va_list ap;
148 	char buf[2048];
149 	int lenBuf;
150 
151 	va_start(ap, fmt);
152 	lenBuf = vsnprintf(buf, sizeof(buf), fmt, ap);
153 	if(lenBuf < 0) {
154 		strncpy(buf, "error message lost due to problem with vsnprintf", sizeof(buf));
155 	}
156 	va_end(ap);
157 	buf[sizeof(buf) - 1] = '\0'; /* just to be on the safe side... */
158 
159 	doLogMsg(iErrno, iErrCode, LOG_ERR, buf);
160 }
161 
162 /* We now receive three parameters: one is the internal error code
163  * which will also become the error message number, the second is
164  * errno - if it is non-zero, the corresponding error message is included
165  * in the text and finally the message text itself. Note that it is not
166  * 100% clean to use the internal errcode, as it may be reached from
167  * multiple actual error causes. However, it is much better than having
168  * no error code at all (and in most cases, a single internal error code
169  * maps to a specific error event).
170  * rgerhards, 2008-06-27
171  */
172 void __attribute__((format(printf, 4, 5)))
LogMsg(const int iErrno,const int iErrCode,const int severity,const char * fmt,...)173 LogMsg(const int iErrno, const int iErrCode, const int severity, const char *fmt, ... )
174 {
175 	va_list ap;
176 	char buf[2048];
177 	int lenBuf;
178 
179 	va_start(ap, fmt);
180 	lenBuf = vsnprintf(buf, sizeof(buf), fmt, ap);
181 	if(lenBuf < 0) {
182 		strncpy(buf, "error message lost due to problem with vsnprintf", sizeof(buf));
183 	}
184 	va_end(ap);
185 	buf[sizeof(buf) - 1] = '\0'; /* just to be on the safe side... */
186 
187 	doLogMsg(iErrno, iErrCode, severity, buf);
188 }
189 
190 
191 /* Write an oversize message to the oversize message error log.
192  * We do NOT handle errors during writing that log other than emitting
193  * yet another error message. The reason is that there really is nothing
194  * else that we could do in that case.
195  * rgerhards, 2018-05-03
196  */
ATTR_NONNULL()197 rsRetVal ATTR_NONNULL()
198 writeOversizeMessageLog(const smsg_t *const pMsg)
199 {
200 	struct json_object *json = NULL;
201 	char *rendered = NULL;
202 	struct json_object *jval;
203 	uchar *buf;
204 	size_t toWrite;
205 	ssize_t wrRet;
206 	int dummy;
207 	int mutexLocked = 0;
208 	DEFiRet;
209 	ISOBJ_TYPE_assert(pMsg, msg);
210 
211 	if(glblGetOversizeMsgErrorFile() == NULL) {
212 		FINALIZE;
213 	}
214 
215 	pthread_mutex_lock(&oversizeMsgLogMut);
216 	mutexLocked = 1;
217 
218 	if(fdOversizeMsgLog == -1) {
219 		fdOversizeMsgLog = open((char*)glblGetOversizeMsgErrorFile(),
220 					O_WRONLY|O_CREAT|O_APPEND|O_LARGEFILE|O_CLOEXEC,
221 					S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
222 		if(fdOversizeMsgLog == -1) {
223 			LogError(errno, RS_RET_ERR, "error opening oversize message log file %s",
224 				glblGetOversizeMsgErrorFile());
225 			FINALIZE;
226 		}
227 	}
228 
229 	assert(fdOversizeMsgLog != -1);
230 	json = json_object_new_object();
231 	if(json == NULL) {
232 		FINALIZE;
233 	}
234 
235 	getRawMsg(pMsg, &buf, &dummy);
236 	jval = json_object_new_string((char*)buf);
237 	json_object_object_add(json, "rawmsg", jval);
238 
239 	getInputName(pMsg, &buf, &dummy);
240 	jval = json_object_new_string((char*)buf);
241 	json_object_object_add(json, "input", jval);
242 
243 	CHKmalloc(rendered = strdup((char*)fjson_object_to_json_string(json)));
244 
245 	toWrite = strlen(rendered) + 1;
246 	/* Note: we overwrite the '\0' terminator with '\n' -- so we avoid
247 	 * calling malloc() -- write() does NOT need '\0'!
248 	 */
249 	rendered[toWrite-1] = '\n'; /* NO LONGER A STRING! */
250 	wrRet = write(fdOversizeMsgLog, rendered, toWrite);
251 	if(wrRet != (ssize_t) toWrite) {
252 		LogError(errno, RS_RET_IO_ERROR,
253 			"error writing oversize message log file %s, write returned %lld",
254 			glblGetOversizeMsgErrorFile(), (long long) wrRet);
255 	}
256 
257 finalize_it:
258 	free(rendered);
259 	if(mutexLocked) {
260 		pthread_mutex_unlock(&oversizeMsgLogMut);
261 	}
262 	if(json != NULL) {
263 		fjson_object_put(json);
264 	}
265 	RETiRet;
266 }
267 
268 
269 void
errmsgDoHUP(void)270 errmsgDoHUP(void)
271 {
272 	pthread_mutex_lock(&oversizeMsgLogMut);
273 	if(fdOversizeMsgLog != -1) {
274 		close(fdOversizeMsgLog);
275 		fdOversizeMsgLog = -1;
276 	}
277 	pthread_mutex_unlock(&oversizeMsgLogMut);
278 }
279 
280 
281 void
errmsgExit(void)282 errmsgExit(void)
283 {
284 	if(fdOversizeMsgLog != -1) {
285 		close(fdOversizeMsgLog);
286 	}
287 }
288