/* The errmsg object. * * Module begun 2008-03-05 by Rainer Gerhards, based on some code * from syslogd.c. I converted this module to lgpl and have checked that * all contributors agreed to that step. * Now moving to ASL 2.0, and contributor checks tell that there is no need * to take further case, as the code now boils to be either my own or, a few lines, * of the original BSD-licenses sysklogd code. rgerhards, 2012-01-16 * * Copyright 2008-2018 Adiscon GmbH. * * This file is part of the rsyslog runtime library. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * -or- * see COPYING.ASL20 in the source distribution * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "rsyslog.h" #include "obj.h" #include "msg.h" #include "errmsg.h" #include "operatingstate.h" #include "srUtils.h" #include "stringbuf.h" #include "rsconf.h" /* static data */ #ifndef O_LARGEFILE #define O_LARGEFILE 0 #endif static int bHadErrMsgs; /* indicates if we had error messages since reset of this flag * This is used to abort a run if the config is unclean. */ static int fdOversizeMsgLog = -1; static pthread_mutex_t oversizeMsgLogMut = PTHREAD_MUTEX_INITIALIZER; /* ------------------------------ methods ------------------------------ */ /* Resets the error message flag. Must be done before processing config * files. */ void resetErrMsgsFlag(void) { bHadErrMsgs = 0; } int hadErrMsgs(void) { return bHadErrMsgs; } /* We now receive three parameters: one is the internal error code * which will also become the error message number, the second is * errno - if it is non-zero, the corresponding error message is included * in the text and finally the message text itself. Note that it is not * 100% clean to use the internal errcode, as it may be reached from * multiple actual error causes. However, it is much better than having * no error code at all (and in most cases, a single internal error code * maps to a specific error event). * rgerhards, 2008-06-27 */ static void doLogMsg(const int iErrno, const int iErrCode, const int severity, const char *msg) { char buf[2048]; char errStr[1024]; dbgprintf("Called LogMsg, msg: %s\n", msg); osf_write(OSF_TAG_MSG, msg); if(iErrno != 0) { rs_strerror_r(iErrno, errStr, sizeof(errStr)); if(iErrCode == NO_ERRCODE || iErrCode == RS_RET_ERR) { snprintf(buf, sizeof(buf), "%s: %s [v%s]", msg, errStr, VERSION); } else { snprintf(buf, sizeof(buf), "%s: %s [v%s try https://www.rsyslog.com/e/%d ]", msg, errStr, VERSION, iErrCode * -1); } } else { if(iErrCode == NO_ERRCODE || iErrCode == RS_RET_ERR) { snprintf(buf, sizeof(buf), "%s [v%s]", msg, VERSION); } else { snprintf(buf, sizeof(buf), "%s [v%s try https://www.rsyslog.com/e/%d ]", msg, VERSION, iErrCode * -1); } } buf[sizeof(buf) - 1] = '\0'; /* just to be on the safe side... */ errno = 0; const int msglen = (int) strlen(buf); if(msglen > glblGetMaxLine(ourConf)) { /* in extreme cases, our error messages may be longer than the configured * max message size. If so, we just truncate without further indication, as * anything else would probably lead to a death loop on error messages. * Note that we do not split, as we really do not anticipate there is * much value in supporting extremely short max message sizes - we assume * it's just a testbench thing. -- rgerhards, 2018-05-11 */ buf[glblGetMaxLine(ourConf)] = '\0'; /* space must be available! */ } glblErrLogger(severity, iErrCode, (uchar*)buf); if(severity == LOG_ERR) bHadErrMsgs = 1; } /* We now receive three parameters: one is the internal error code * which will also become the error message number, the second is * errno - if it is non-zero, the corresponding error message is included * in the text and finally the message text itself. Note that it is not * 100% clean to use the internal errcode, as it may be reached from * multiple actual error causes. However, it is much better than having * no error code at all (and in most cases, a single internal error code * maps to a specific error event). * rgerhards, 2008-06-27 */ void __attribute__((format(printf, 3, 4))) LogError(const int iErrno, const int iErrCode, const char *fmt, ... ) { va_list ap; char buf[2048]; int lenBuf; va_start(ap, fmt); lenBuf = vsnprintf(buf, sizeof(buf), fmt, ap); if(lenBuf < 0) { strncpy(buf, "error message lost due to problem with vsnprintf", sizeof(buf)); } va_end(ap); buf[sizeof(buf) - 1] = '\0'; /* just to be on the safe side... */ doLogMsg(iErrno, iErrCode, LOG_ERR, buf); } /* We now receive three parameters: one is the internal error code * which will also become the error message number, the second is * errno - if it is non-zero, the corresponding error message is included * in the text and finally the message text itself. Note that it is not * 100% clean to use the internal errcode, as it may be reached from * multiple actual error causes. However, it is much better than having * no error code at all (and in most cases, a single internal error code * maps to a specific error event). * rgerhards, 2008-06-27 */ void __attribute__((format(printf, 4, 5))) LogMsg(const int iErrno, const int iErrCode, const int severity, const char *fmt, ... ) { va_list ap; char buf[2048]; int lenBuf; va_start(ap, fmt); lenBuf = vsnprintf(buf, sizeof(buf), fmt, ap); if(lenBuf < 0) { strncpy(buf, "error message lost due to problem with vsnprintf", sizeof(buf)); } va_end(ap); buf[sizeof(buf) - 1] = '\0'; /* just to be on the safe side... */ doLogMsg(iErrno, iErrCode, severity, buf); } /* Write an oversize message to the oversize message error log. * We do NOT handle errors during writing that log other than emitting * yet another error message. The reason is that there really is nothing * else that we could do in that case. * rgerhards, 2018-05-03 */ rsRetVal ATTR_NONNULL() writeOversizeMessageLog(const smsg_t *const pMsg) { struct json_object *json = NULL; char *rendered = NULL; struct json_object *jval; uchar *buf; size_t toWrite; ssize_t wrRet; int dummy; int mutexLocked = 0; DEFiRet; ISOBJ_TYPE_assert(pMsg, msg); if(glblGetOversizeMsgErrorFile(runConf) == NULL) { FINALIZE; } pthread_mutex_lock(&oversizeMsgLogMut); mutexLocked = 1; if(fdOversizeMsgLog == -1) { fdOversizeMsgLog = open((char*)glblGetOversizeMsgErrorFile(runConf), O_WRONLY|O_CREAT|O_APPEND|O_LARGEFILE|O_CLOEXEC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); if(fdOversizeMsgLog == -1) { LogError(errno, RS_RET_ERR, "error opening oversize message log file %s", glblGetOversizeMsgErrorFile(runConf)); FINALIZE; } } assert(fdOversizeMsgLog != -1); json = json_object_new_object(); if(json == NULL) { FINALIZE; } getRawMsg(pMsg, &buf, &dummy); jval = json_object_new_string((char*)buf); json_object_object_add(json, "rawmsg", jval); getInputName(pMsg, &buf, &dummy); jval = json_object_new_string((char*)buf); json_object_object_add(json, "input", jval); CHKmalloc(rendered = strdup((char*)fjson_object_to_json_string(json))); toWrite = strlen(rendered) + 1; /* Note: we overwrite the '\0' terminator with '\n' -- so we avoid * calling malloc() -- write() does NOT need '\0'! */ rendered[toWrite-1] = '\n'; /* NO LONGER A STRING! */ wrRet = write(fdOversizeMsgLog, rendered, toWrite); if(wrRet != (ssize_t) toWrite) { LogError(errno, RS_RET_IO_ERROR, "error writing oversize message log file %s, write returned %lld", glblGetOversizeMsgErrorFile(runConf), (long long) wrRet); } finalize_it: free(rendered); if(mutexLocked) { pthread_mutex_unlock(&oversizeMsgLogMut); } if(json != NULL) { fjson_object_put(json); } RETiRet; } void errmsgDoHUP(void) { pthread_mutex_lock(&oversizeMsgLogMut); if(fdOversizeMsgLog != -1) { close(fdOversizeMsgLog); fdOversizeMsgLog = -1; } pthread_mutex_unlock(&oversizeMsgLogMut); } void errmsgExit(void) { if(fdOversizeMsgLog != -1) { close(fdOversizeMsgLog); } }