diff --git a/Makefile.in b/Makefile.in index 7344ee4c1..4a10c3654 100644 --- a/Makefile.in +++ b/Makefile.in @@ -34,11 +34,11 @@ FROMDOS=/home/cmunk/bin/4dos # #XCFLAGS=-O -g -export-dynamic -IRCDLIBS=@IRCDLIBS@ @PCRE2_LIBS@ @ARGON2_LIBS@ @CARES_LIBS@ @SODIUM_LIBS@ @PTHREAD_LIBS@ +IRCDLIBS=@IRCDLIBS@ @PCRE2_LIBS@ @ARGON2_LIBS@ @CARES_LIBS@ @SODIUM_LIBS@ @JANSSON_LIBS@ @PTHREAD_LIBS@ CRYPTOLIB=@CRYPTOLIB@ OPENSSLINCLUDES= -XCFLAGS=@PTHREAD_CFLAGS@ @PCRE2_CFLAGS@ @ARGON2_CFLAGS@ @CARES_CFLAGS@ @SODIUM_CFLAGS@ @CFLAGS@ @HARDEN_CFLAGS@ @CPPFLAGS@ +XCFLAGS=@PTHREAD_CFLAGS@ @PCRE2_CFLAGS@ @ARGON2_CFLAGS@ @CARES_CFLAGS@ @SODIUM_CFLAGS@ @JANSSON_CFLAGS@ @CFLAGS@ @HARDEN_CFLAGS@ @CPPFLAGS@ # # use the following on MIPS: #CFLAGS= -systype bsd43 -DSYSTYPE_BSD43 -I$(INCLUDEDIR) diff --git a/configure b/configure index 2a3a24912..f771bd074 100755 --- a/configure +++ b/configure @@ -639,6 +639,8 @@ build_os build_vendor build_cpu build +JANSSON_LIBS +JANSSON_CFLAGS CARES_LIBS CARES_CFLAGS SODIUM_LIBS @@ -754,6 +756,7 @@ with_system_pcre2 with_system_argon2 with_system_sodium with_system_cares +with_system_jansson enable_ssl enable_dynamic_linking enable_werror @@ -779,7 +782,9 @@ ARGON2_LIBS SODIUM_CFLAGS SODIUM_LIBS CARES_CFLAGS -CARES_LIBS' +CARES_LIBS +JANSSON_CFLAGS +JANSSON_LIBS' # Initialize some variables set by options. @@ -1459,6 +1464,9 @@ Optional Packages: library. Normally autodetected via pkg-config --without-system-cares Use bundled version instead of system c-ares. Normally autodetected via pkg-config. + --without-system-jansson + Use bundled version instead of system jansson. + Normally autodetected via pkg-config. Some influential environment variables: CC C compiler command @@ -1486,6 +1494,10 @@ Some influential environment variables: CARES_CFLAGS C compiler flags for CARES, overriding pkg-config CARES_LIBS linker flags for CARES, overriding pkg-config + JANSSON_CFLAGS + C compiler flags for JANSSON, overriding pkg-config + JANSSON_LIBS + linker flags for JANSSON, overriding pkg-config Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. @@ -6528,6 +6540,14 @@ else fi +# Check whether --with-system-jansson was given. +if test "${with_system_jansson+set}" = set; then : + withval=$with_system_jansson; +else + with_system_jansson=yes +fi + + # Check whether --enable-ssl was given. if test "${enable_ssl+set}" = set; then : enableval=$enable_ssl; @@ -7720,6 +7740,130 @@ cd $cur_dir fi +has_system_jansson="no" +if test "x$with_system_jansson" = "xyes"; then : + + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for JANSSON" >&5 +$as_echo_n "checking for JANSSON... " >&6; } + +if test -n "$JANSSON_CFLAGS"; then + pkg_cv_JANSSON_CFLAGS="$JANSSON_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"jansson >= 2.0.0\""; } >&5 + ($PKG_CONFIG --exists --print-errors "jansson >= 2.0.0") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_JANSSON_CFLAGS=`$PKG_CONFIG --cflags "jansson >= 2.0.0" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$JANSSON_LIBS"; then + pkg_cv_JANSSON_LIBS="$JANSSON_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"jansson >= 2.0.0\""; } >&5 + ($PKG_CONFIG --exists --print-errors "jansson >= 2.0.0") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_JANSSON_LIBS=`$PKG_CONFIG --libs "jansson >= 2.0.0" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + JANSSON_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "jansson >= 2.0.0" 2>&1` + else + JANSSON_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "jansson >= 2.0.0" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$JANSSON_PKG_ERRORS" >&5 + + has_system_jansson=no +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + has_system_jansson=no +else + JANSSON_CFLAGS=$pkg_cv_JANSSON_CFLAGS + JANSSON_LIBS=$pkg_cv_JANSSON_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + has_system_jansson=yes +if test "x$PRIVATELIBDIR" != "x"; then : + rm -f "$PRIVATELIBDIR/"libjansson* +fi +fi +fi + +if test "$has_system_jansson" = "no"; then : + +jansson_version="2.13.1" +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: extracting jansson library" >&5 +$as_echo "extracting jansson library" >&6; } +cur_dir=`pwd` +cd extras +rm -rf jansson-$jansson_version jansson +if test "x$ac_cv_path_GUNZIP" = "x" ; then + tar xfz jansson.tar.gz +else + cp jansson.tar.gz jansson.tar.gz.bak + gunzip -f jansson.tar.gz + cp jansson.tar.gz.bak jansson.tar.gz + tar xf jansson.tar +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: compiling jansson library" >&5 +$as_echo "compiling jansson library" >&6; } +cd jansson-$jansson_version +save_cflags="$CFLAGS" +CFLAGS="$orig_cflags" +export CFLAGS +./configure --prefix=$cur_dir/extras/jansson --libdir=$PRIVATELIBDIR --enable-shared --disable-static --enable-opt || exit 1 +CFLAGS="$save_cflags" +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: compiling jansson resolver library" >&5 +$as_echo "compiling jansson resolver library" >&6; } +$ac_cv_prog_MAKER || exit 1 +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: installing jansson resolver library" >&5 +$as_echo "installing jansson resolver library" >&6; } +$ac_cv_prog_MAKER install || exit 1 +JANSSON_CFLAGS="-I$cur_dir/extras/jansson/include" + +JANSSON_LIBS= +if test -n "$ac_cv_path_PKGCONFIG"; then : + JANSSON_LIBS="`$ac_cv_path_PKGCONFIG --libs jansson.pc`" +fi +if test -z "$JANSSON_LIBS"; then : + JANSSON_LIBS="-L$PRIVATELIBDIR -ljansson" +fi + +cd $cur_dir + +fi + + # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 diff --git a/configure.ac b/configure.ac index 47975370c..b2b143fd2 100644 --- a/configure.ac +++ b/configure.ac @@ -516,6 +516,7 @@ AC_ARG_WITH(system-pcre2, [AS_HELP_STRING([--without-system-pcre2], [Use the sys AC_ARG_WITH(system-argon2, [AS_HELP_STRING([--without-system-argon2], [Use bundled version instead of system argon2 library. Normally autodetected via pkg-config])], [], [with_system_argon2=yes]) AC_ARG_WITH(system-sodium, [AS_HELP_STRING([--without-system-sodium], [Use bundled version instead of system sodium library. Normally autodetected via pkg-config])], [], [with_system_sodium=yes]) AC_ARG_WITH(system-cares, [AS_HELP_STRING([--without-system-cares], [Use bundled version instead of system c-ares. Normally autodetected via pkg-config.])], [], [with_system_cares=yes]) +AC_ARG_WITH(system-jansson, [AS_HELP_STRING([--without-system-jansson], [Use bundled version instead of system jansson. Normally autodetected via pkg-config.])], [], [with_system_jansson=yes]) CHECK_SSL CHECK_SSL_CTX_SET1_CURVES_LIST CHECK_SSL_CTX_SET_MIN_PROTO_VERSION @@ -766,6 +767,57 @@ AC_SUBST(CARES_LIBS) cd $cur_dir ]) +dnl Use system jansson when available, unless --without-system-jansson +has_system_jansson="no" +AS_IF([test "x$with_system_jansson" = "xyes"],[ +PKG_CHECK_MODULES([JANSSON], [jansson >= 2.0.0],[has_system_jansson=yes +AS_IF([test "x$PRIVATELIBDIR" != "x"], [rm -f "$PRIVATELIBDIR/"libjansson*])],[has_system_jansson=no])]) + +AS_IF([test "$has_system_jansson" = "no"],[ +dnl REMEMBER TO CHANGE WITH A NEW JANSSON RELEASE! +jansson_version="2.13.1" +AC_MSG_RESULT(extracting jansson library) +cur_dir=`pwd` +cd extras +dnl remove old jansson directory to force a recompile... +dnl and remove its installation prefix just to clean things up. +rm -rf jansson-$jansson_version jansson +if test "x$ac_cv_path_GUNZIP" = "x" ; then + tar xfz jansson.tar.gz +else + cp jansson.tar.gz jansson.tar.gz.bak + gunzip -f jansson.tar.gz + cp jansson.tar.gz.bak jansson.tar.gz + tar xf jansson.tar +fi +AC_MSG_RESULT(compiling jansson library) +cd jansson-$jansson_version +save_cflags="$CFLAGS" +CFLAGS="$orig_cflags" +export CFLAGS +./configure --prefix=$cur_dir/extras/jansson --libdir=$PRIVATELIBDIR --enable-shared --disable-static --enable-opt || exit 1 +CFLAGS="$save_cflags" +AC_MSG_RESULT(compiling jansson resolver library) +$ac_cv_prog_MAKER || exit 1 +AC_MSG_RESULT(installing jansson resolver library) +$ac_cv_prog_MAKER install || exit 1 +JANSSON_CFLAGS="-I$cur_dir/extras/jansson/include" +AC_SUBST(JANSSON_CFLAGS) +JANSSON_LIBS= +dnl See c-ares's compilation section for more info on this hack. +dnl ensure that we're linking against the bundled version +dnl (we only reach this code if linking against the bundled version is desired). +AS_IF([test -n "$ac_cv_path_PKGCONFIG"], + [JANSSON_LIBS="`$ac_cv_path_PKGCONFIG --libs jansson.pc`"]) +dnl ^^^ FIXME FIXME this is likely incorrect the .pc etc +dnl For when pkg-config isn't available +AS_IF([test -z "$JANSSON_LIBS"], + [JANSSON_LIBS="-L$PRIVATELIBDIR -ljansson"]) +AC_SUBST(JANSSON_LIBS) +cd $cur_dir +]) + + AX_PTHREAD() CHECK_LIBCURL diff --git a/extras/jansson.tar.gz b/extras/jansson.tar.gz new file mode 100644 index 000000000..bd1d16c2d Binary files /dev/null and b/extras/jansson.tar.gz differ diff --git a/include/h.h b/include/h.h index ac52c5367..1a4530388 100644 --- a/include/h.h +++ b/include/h.h @@ -1079,3 +1079,15 @@ extern FloodSettings *find_floodsettings_block(const char *name); extern FloodSettings *get_floodsettings_for_user(Client *client, FloodOption opt); extern MODVAR char *floodoption_names[]; extern void flood_limit_exceeded_log(Client *client, char *floodname); +/* logging */ +#ifdef DEBUGMODE +#define unreal_log(...) do_unreal_log(__VA_ARGS__, log_data_source(__FILE__, __LINE__, __FUNCTION__), NULL) +#else +#define unreal_log(...) do_unreal_log(__VA_ARGS__, NULL) +#endif +extern void do_unreal_log(LogLevel loglevel, char *subsystem, char *event_id, Client *client, char *msg, ...); +extern LogData *log_data_string(const char *key, const char *str); +extern LogData *log_data_integer(const char *key, int64_t integer); +extern LogData *log_data_client(const char *key, Client *client); +extern LogData *log_data_source(const char *file, int line, const char *function); +/* end of logging */ diff --git a/include/struct.h b/include/struct.h index b942e771a..3c0699a86 100644 --- a/include/struct.h +++ b/include/struct.h @@ -39,6 +39,7 @@ #include #include #include +#include #include "common.h" #include "sys.h" #include @@ -214,6 +215,32 @@ typedef OperPermission (*OperClassEntryEvalCallback)(OperClassACLEntryVar* varia #define LOG_SPAMFILTER 0x0400 #define LOG_FLOOD 0x0800 +typedef enum LogFieldType { + LOG_FIELD_INTEGER, // and unsigned? + LOG_FIELD_STRING, + LOG_FIELD_CLIENT, + LOG_FIELD_OBJECT +} LogFieldType; + +typedef struct LogData { + LogFieldType type; + char *key; + union { + int64_t integer; + char *string; + Client *client; + json_t *object; + } value; +} LogData; + +/** New log levels for unreal_log() */ +typedef enum LogLevel { + ULOG_INFO = 0, + ULOG_WARN = 1, + ULOG_ERROR = 2, + ULOG_FATAL = 3 +} LogLevel; + /* ** 'offsetof' is defined in ANSI-C. The following definition ** is not absolutely portable (I have been told), but so far diff --git a/src/Makefile.in b/src/Makefile.in index a50e095d8..d339fee60 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -32,7 +32,7 @@ OBJS=dns.o auth.o channel.o crule.o dbuf.o \ api-clicap.o api-messagetag.o api-history-backend.o api-efunctions.o \ api-event.o \ crypt_blowfish.o unrealdb.o updconf.o crashreport.o modulemanager.o \ - utf8.o \ + utf8.o log.o \ openssl_hostname_validation.o $(URL) SRC=$(OBJS:%.o=%.c) @@ -239,6 +239,9 @@ modulemanager.o: modulemanager.c $(INCLUDES) utf8.o: utf8.c $(INCLUDES) $(CC) $(CFLAGS) $(BINCFLAGS) -c utf8.c +log.o: log.c $(INCLUDES) + $(CC) $(CFLAGS) $(BINCFLAGS) -c log.c + openssl_hostname_validation.o: openssl_hostname_validation.c $(INCLUDES) $(CC) $(CFLAGS) $(BINCFLAGS) -c openssl_hostname_validation.c diff --git a/src/ircd.c b/src/ircd.c index d4b12022c..4c9fb3a89 100644 --- a/src/ircd.c +++ b/src/ircd.c @@ -958,8 +958,12 @@ int InitUnrealIRCd(int argc, char *argv[]) #if 1 case 'S': //charsys_dump_table(p ? p : "*"); - unrealdb_test(); + //unrealdb_test(); + { + extern void logtest(void); + logtest(); exit(0); + } #endif #ifndef _WIN32 case 't': diff --git a/src/log.c b/src/log.c new file mode 100644 index 000000000..b128fcfba --- /dev/null +++ b/src/log.c @@ -0,0 +1,357 @@ +/************************************************************************ + * IRC - Internet Relay Chat, src/api-channelmode.c + * (C) 2021 Bram Matthys (Syzop) and the UnrealIRCd Team + * + * See file AUTHORS in IRC package for additional names of + * the programmers. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** @file + * @brief The logging API + */ + +#include "unrealircd.h" + +// TODO: validate that all 'key' values are lowercase+underscore+digits in all functions below. + +void json_expand_client(json_t *j, char *key, Client *client, int detail) +{ + json_t *child = json_object(); + json_object_set_new(j, key, child); + + json_object_set_new(child, "name", json_string(client->name)); + + if (client->user) + json_object_set_new(child, "username", json_string(client->user->username)); + + if (client->user && *client->user->realhost) + json_object_set_new(child, "host", json_string(client->user->realhost)); + else if (client->local && *client->local->sockhost) + json_object_set_new(child, "host", json_string(client->local->sockhost)); + else + json_object_set_new(child, "host", json_string(GetIP(client))); + + json_object_set_new(child, "ip", json_string(GetIP(client))); + + if (IsLoggedIn(client)) + json_object_set_new(child, "account", json_string(client->user->svid)); +} + +void json_expand_channel(json_t *j, char *key, Channel *channel, int detail) +{ + json_t *child = json_object(); + json_object_set_new(j, key, child); + json_object_set_new(child, "name", json_string(channel->chname)); +} + +char *timestamp_iso8601(void) +{ + struct timeval t; + struct tm *tm; + time_t sec; + static char buf[64]; + + gettimeofday(&t, NULL); + sec = t.tv_sec; + tm = gmtime(&sec); + snprintf(buf, sizeof(buf), "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", + tm->tm_year + 1900, + tm->tm_mon + 1, + tm->tm_mday, + tm->tm_hour, + tm->tm_min, + tm->tm_sec, + (int)(t.tv_usec / 1000)); + return buf; +} + +LogData *log_data_string(const char *key, const char *str) +{ + LogData *d = safe_alloc(sizeof(LogData)); + d->type = LOG_FIELD_STRING; + safe_strdup(d->key, key); + safe_strdup(d->value.string, str); + return d; +} + +LogData *log_data_integer(const char *key, int64_t integer) +{ + LogData *d = safe_alloc(sizeof(LogData)); + d->type = LOG_FIELD_INTEGER; + safe_strdup(d->key, key); + d->value.integer = integer; + return d; +} + +LogData *log_data_client(const char *key, Client *client) +{ + LogData *d = safe_alloc(sizeof(LogData)); + d->type = LOG_FIELD_CLIENT; + safe_strdup(d->key, key); + d->value.client = client; + return d; +} + +LogData *log_data_source(const char *file, int line, const char *function) +{ + LogData *d = safe_alloc(sizeof(LogData)); + json_t *j; + + d->type = LOG_FIELD_OBJECT; + safe_strdup(d->key, "source"); + d->value.object = j = json_object(); + json_object_set_new(j, "file", json_string(file)); + json_object_set_new(j, "line", json_integer(line)); + json_object_set_new(j, "function", json_string(function)); + return d; +} + +void log_data_free(LogData *d) +{ + if (d->type == LOG_FIELD_STRING) + safe_free(d->value.string); + safe_free(d->key); + safe_free(d); +} + +char *loglevel_to_string(LogLevel loglevel) +{ + switch(loglevel) + { + case ULOG_INFO: + return "info"; + case ULOG_WARN: + return "warn"; + case ULOG_ERROR: + return "error"; + case ULOG_FATAL: + return "fatal"; + default: + return "???"; + } +} + +/** Build a string and replace $variables where needed. + * See src/modules/blacklist.c for an example. + * @param inbuf The input string + * @param outbuf The output string + * @param len The maximum size of the output string (including NUL) + * @param name Array of variables names + * @param value Array of variable values + */ +void buildlogstring(const char *inbuf, char *outbuf, size_t len, json_t *details) +{ + const char *i, *p; + char *o; + int left = len - 1; + int cnt, found; + char varname[256], *varp; + json_t *t; + +#ifdef DEBUGMODE + if (len <= 0) + abort(); +#endif + + for (i = inbuf, o = outbuf; *i; i++) + { + if (*i == '$') + { + i++; + + /* $$ = literal $ */ + if (*i == '$') + goto literal; + + if (!isalnum(*i)) + { + /* What do we do with things like '$/' ? -- treat literal */ + i--; + goto literal; + } + + /* find termination */ + for (p=i; isalnum(*p) || ((*p == '.') && isalnum(p[1])); p++); + + /* find variable name in list */ + *varname = '\0'; + strlncat(varname, i, sizeof(varname), p - i); + varp = strchr(varname, '.'); + if (varp) + *varp++ = '\0'; + t = json_object_get(details, varname); + if (t) + { + const char *output = NULL; + if (varp) + { + /* Fetch explicit object.key */ + t = json_object_get(t, varp); + if (t) + output = json_string_value(t); + } else + if (json_is_object(t)) + { + /* Fetch object.name */ + t = json_object_get(t, "name"); + if (t) + output = json_string_value(t); + } else + { + output = json_string_value(t); + } + if (output) + { + strlcpy(o, output, left); + left -= strlen(output); /* may become <0 */ + if (left <= 0) + return; /* return - don't write \0 to 'o'. ensured by strlcpy already */ + o += strlen(output); /* value entirely written */ + } + } else + { + /* variable name does not exist -- treat as literal string */ + i--; + goto literal; + } + + /* value written. we're done. */ + i = p - 1; + continue; + } +literal: + if (!left) + break; + *o++ = *i; + left--; + if (!left) + break; + } + *o = '\0'; +} + +void do_unreal_log(LogLevel loglevel, char *subsystem, char *event_id, + Client *client, + char *msg, ...) +{ + va_list vl; + LogData *d; + char *str; + json_t *j = NULL; + json_t *j_details = NULL; + const char *fmt_name[32], *fmt_value[32]; + char msgbuf[1024]; + + /* TODO: Enforcement: + * - loglevel must be valid + * - subsystem may only contain lowercase, underscore and numbers + * - event_id may only contain UPPERCASE, underscore and numbers (but not start with a number) + * - msg may not contain percent signs (%) as that is an obvious indication something is wrong? + * or maybe a temporary restriction while upgrading that can be removed later ;) + */ + memset(&fmt_name, 0, sizeof(fmt_name)); + memset(&fmt_value, 0, sizeof(fmt_value)); + + j = json_object(); + j_details = json_object(); + + json_object_set_new(j, "timestamp", json_string(timestamp_iso8601())); + json_object_set_new(j, "level", json_string(loglevel_to_string(loglevel))); + json_object_set_new(j, "subsystem", json_string(subsystem)); + json_object_set_new(j, "event_id", json_string(event_id)); + + /* We put all the rest in j_details because we want to enforce + * a certain ordering of the JSON output. We will merge these + * details later on. + */ + if (client) + json_expand_client(j_details, "client", client, 0); + /* Additional details (if any) */ + va_start(vl, msg); + while ((d = va_arg(vl, LogData *))) + { + switch(d->type) + { + case LOG_FIELD_INTEGER: + json_object_set_new(j_details, d->key, json_integer(d->value.integer)); + break; + case LOG_FIELD_STRING: + json_object_set_new(j_details, d->key, json_string(d->value.string)); + break; + case LOG_FIELD_CLIENT: + json_expand_client(j_details, d->key, d->value.client, 0); + break; + case LOG_FIELD_OBJECT: + json_object_set_new(j_details, d->key, d->value.object); + break; + default: +#ifdef DEBUGMODE + abort(); +#endif + break; + } + log_data_free(d); + } + // todo: use a special buildvarstring() so we don't end up strdup'ing lots of stuff that is unused anyway + fmt_name[0] = "client"; + fmt_value[0] = client ? client->name : ""; + *msgbuf = '\0'; + //buildvarstring(msg, msgbuf, sizeof(msgbuf), fmt_name, fmt_value); + buildlogstring(msg, msgbuf, sizeof(msgbuf), j_details); + json_object_set_new(j, "msg", json_string(msgbuf)); + + /* Now merge the details into root object 'j': */ + json_object_update_missing(j, j_details); + + str = json_dumps(j, 0); + ircd_log(LOG_ERROR, "STD-STR: %s %s %s %s", loglevel_to_string(loglevel), subsystem, event_id, msgbuf); + ircd_log(LOG_ERROR, "JSON-STR: %s", str); + free(str); + + json_decref(j_details); + json_decref(j); +} + +void simpletest(void) +{ + char *str; + json_t *j = json_object(); + json_t *j_client = json_array(); + + json_object_set_new(j, "id", json_integer(1)); + json_object_set_new(j, "data", j_client); + json_array_append_new(j_client, json_integer(1)); + json_array_append_new(j_client, json_integer(2)); + json_array_append_new(j_client, json_integer(3)); + + str = json_dumps(j, 0); + printf("RESULT:\n%s\n", str); + free(str); + + json_decref(j); +} + +void logtest(void) +{ + strcpy(me.name, "irc.test.net"); + unreal_log(ULOG_INFO, "test", "TEST", &me, "Hello there!"); + unreal_log(ULOG_INFO, "test", "TEST", &me, "Hello there i like $client!"); + unreal_log(ULOG_INFO, "test", "TEST", &me, "Hello there i like $client with IP $client.ip!"); + unreal_log(ULOG_INFO, "test", "TEST", &me, "More data!", log_data_string("fun", "yes lots of fun")); + unreal_log(ULOG_INFO, "test", "TEST", &me, "More data, fun: $fun!", log_data_string("fun", "yes lots of fun"), log_data_integer("some_integer", 1337)); + unreal_log(ULOG_INFO, "sacmds", "SAJOIN_COMMAND", &me, "Client $client used SAJOIN to join $target to y!", log_data_client("target", &me)); +} diff --git a/src/modules/sajoin.c b/src/modules/sajoin.c index b41f1ae77..3d66b5d9c 100644 --- a/src/modules/sajoin.c +++ b/src/modules/sajoin.c @@ -97,7 +97,9 @@ CMD_FUNC(cmd_sajoin) /* Logging function added by XeRXeS */ ircd_log(LOG_SACMDS,"SAJOIN: %s used SAJOIN to make %s join %s", client->name, target->name, parv[2]); - + unreal_log(ULOG_INFO, "sacmds", "SAJOIN_COMMAND", client, "SAJOIN: %s used SAJOIN to make %s join %s", + log_data_client("target", target), + log_data_string("channel", parv[2])); return; } @@ -293,6 +295,9 @@ CMD_FUNC(cmd_sajoin) ircd_log(LOG_SACMDS,"SAJOIN: %s used SAJOIN to make %s join %c%s", client->name, target->name, sjmode, jbuf); } + unreal_log(ULOG_INFO, "sacmds", "SAJOIN_COMMAND", client, "SAJOIN: $client used SAJOIN to make $target join $channel", + log_data_client("target", target), + log_data_string("channel", jbuf)); } } }