mirror of
https://github.com/rsyslog/rsyslog.git
synced 2025-12-15 18:40:41 +01:00
most importantly, header file now includes comments that enable tooltip-like behaviour in IDEs. Also includes antipaterns, which is useful for developers and hopefully also for AI to detect them e.g. in code reviews (and get it right in AI-generated code).
269 lines
8.8 KiB
C
269 lines
8.8 KiB
C
/* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <errno.h>
|
|
#include <assert.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#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;
|
|
}
|
|
|
|
/** @internal
|
|
* Build and emit a single diagnostic line.
|
|
*
|
|
* - If iErrno != 0, append strerror(iErrno).
|
|
* - If iErrCode is neither NO_ERRCODE nor RS_RET_ERR, append a help URL.
|
|
* - Truncate to the configured max line length.
|
|
* - Set the "had error" flag for LOG_ERR.
|
|
*/
|
|
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);
|
|
}
|
|
}
|