981 lines
28 KiB
C++
981 lines
28 KiB
C++
/* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License, version 2.0,
|
|
as published by the Free Software Foundation.
|
|
|
|
This program is also distributed with certain software (including
|
|
but not limited to OpenSSL) that is licensed under separate terms,
|
|
as designated in a particular file or component or in included license
|
|
documentation. The authors of MySQL hereby grant you an additional
|
|
permission to link the program and your derivative works with the
|
|
separately licensed software that they have included with MySQL.
|
|
|
|
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, version 2.0, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
|
|
|
|
#include <mysql/components/services/log_builtins.h>
|
|
|
|
#include "log_service_imp.h"
|
|
#include "m_string.h" // native_strncasecmp()/native_strcasecmp()
|
|
#include "my_compiler.h"
|
|
#include "my_io.h"
|
|
#include "my_sys.h"
|
|
#include "mysqld_error.h" // so we can throw ER_LOG_SYSLOG_*
|
|
|
|
#include <mysql/components/component_implementation.h>
|
|
#include <mysql/components/service_implementation.h>
|
|
|
|
#include <mysql/components/services/component_sys_var_service.h>
|
|
#include <mysql/plugin.h>
|
|
|
|
#include "../sql/set_var.h"
|
|
|
|
#ifndef _WIN32
|
|
#include <syslog.h> // LOG_DAEMON etc. -- facility names
|
|
|
|
/*
|
|
Some C libraries offer a variant of this, but we roll our own so we
|
|
won't have to worry about portability.
|
|
*/
|
|
#define LOG_DAEMON_NAME "daemon"
|
|
|
|
/* facilities on unixoid syslog. */
|
|
struct SYSLOG_FACILITY {
|
|
int id;
|
|
const char *name;
|
|
};
|
|
|
|
static SYSLOG_FACILITY syslog_facility[] = {
|
|
{LOG_DAEMON, LOG_DAEMON_NAME}, /* default for mysqld */
|
|
{LOG_USER, "user"}, /* default for mysql command-line client */
|
|
|
|
{LOG_LOCAL0, "local0"},
|
|
{LOG_LOCAL1, "local1"},
|
|
{LOG_LOCAL2, "local2"},
|
|
{LOG_LOCAL3, "local3"},
|
|
{LOG_LOCAL4, "local4"},
|
|
{LOG_LOCAL5, "local5"},
|
|
{LOG_LOCAL6, "local6"},
|
|
{LOG_LOCAL7, "local7"},
|
|
|
|
/* "just in case" */
|
|
{LOG_AUTH, "auth"},
|
|
{LOG_CRON, "cron"},
|
|
{LOG_KERN, "kern"},
|
|
{LOG_LPR, "lpr"},
|
|
{LOG_MAIL, "mail"},
|
|
{LOG_NEWS, "news"},
|
|
{LOG_SYSLOG, "syslog"},
|
|
{LOG_UUCP, "uucp"},
|
|
|
|
#if defined(LOG_FTP)
|
|
{LOG_FTP, "ftp"},
|
|
#endif
|
|
#if defined(LOG_AUTHPRIV)
|
|
{LOG_AUTHPRIV, "authpriv"},
|
|
#endif
|
|
|
|
{-1, NULL}};
|
|
|
|
// variable names
|
|
#define OPT_FAC "facility"
|
|
#define OPT_PID "include_pid"
|
|
#endif
|
|
#define OPT_TAG "tag"
|
|
|
|
static bool inited = false; /**< component initialized */
|
|
|
|
// components we'll be using
|
|
REQUIRES_SERVICE_PLACEHOLDER(component_sys_variable_register);
|
|
REQUIRES_SERVICE_PLACEHOLDER(component_sys_variable_unregister);
|
|
|
|
REQUIRES_SERVICE_PLACEHOLDER(log_builtins);
|
|
REQUIRES_SERVICE_PLACEHOLDER(log_builtins_string);
|
|
REQUIRES_SERVICE_PLACEHOLDER(log_builtins_syseventlog);
|
|
#ifdef _WIN32
|
|
REQUIRES_SERVICE_PLACEHOLDER(log_builtins_tmp);
|
|
#endif
|
|
|
|
// shorter component pointers for legibility
|
|
SERVICE_TYPE(log_builtins) *log_bi = nullptr;
|
|
SERVICE_TYPE(log_builtins_string) *log_bs = nullptr;
|
|
SERVICE_TYPE(log_builtins_syseventlog)
|
|
*log_se = nullptr;
|
|
#ifdef _WIN32
|
|
SERVICE_TYPE(log_builtins_tmp) *log_bt = nullptr;
|
|
#define LOG_TYPE "Eventlog"
|
|
#else
|
|
#define LOG_TYPE "syslog"
|
|
#define MAX_FAC_LEN 32
|
|
#endif
|
|
#define MAX_TAG_LEN 32
|
|
|
|
// internal state
|
|
#ifndef _WIN32
|
|
static int log_syslog_facility = LOG_DAEMON; ///< facility we're syslogging to
|
|
static bool log_syslog_include_pid = true; ///< log process ID
|
|
#endif
|
|
static char *log_syslog_ident = nullptr; ///< ident we're using (see "tag")
|
|
static bool log_syslog_enabled = false; ///< logging engaged
|
|
|
|
// The following describe our system variables (their ranges, defaults, etc.).
|
|
STR_CHECK_ARG(tag) values_tag; ///< syslog default tag
|
|
#ifndef _WIN32
|
|
STR_CHECK_ARG(fac) values_fac; ///< syslog default facility
|
|
BOOL_CHECK_ARG(pid) values_pid; ///< syslog default PID state
|
|
#endif
|
|
|
|
static char *buffer_tag = nullptr; ///< sysvar containing tag, if any
|
|
static char *buffer_fac = nullptr; ///< sysvar containing fac, if any
|
|
|
|
#ifndef _WIN32
|
|
/*
|
|
Logs historically have subtly different names, to meet each platform's
|
|
conventions -- "mysqld" on unix (via mysqld_safe), and "MySQL" for the
|
|
Win NT EventLog.
|
|
*/
|
|
#define PREFIX "mysqld"
|
|
#else
|
|
#define PREFIX "MySQL"
|
|
#endif
|
|
#define MY_NAME "syseventlog"
|
|
|
|
#ifndef _WIN32
|
|
|
|
/**
|
|
On being handed a syslog facility name tries to look it up.
|
|
If successful, fills in a struct with the facility ID and
|
|
the facility's canonical name.
|
|
|
|
@param f Name of the facility we're trying to look up.
|
|
Lookup is case-insensitive; leading "log_" is ignored.
|
|
@param [out] rsf A buffer in which to return the ID and canonical name.
|
|
|
|
@retval false No errors; buffer contains valid result
|
|
@retval true Something went wrong, no valid result set returned
|
|
*/
|
|
static bool log_syslog_find_facility(const char *f, SYSLOG_FACILITY *rsf) {
|
|
if (!f || !*f || !rsf) return true;
|
|
|
|
if (native_strncasecmp(f, "log_", 4) == 0) f += 4;
|
|
|
|
for (int i = 0; syslog_facility[i].name != nullptr; i++)
|
|
if (!native_strcasecmp(f, syslog_facility[i].name)) {
|
|
rsf->id = syslog_facility[i].id;
|
|
rsf->name = syslog_facility[i].name;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#endif
|
|
|
|
/**
|
|
Close POSIX syslog / Windows EventLog.
|
|
|
|
@retval 0 On Success.
|
|
@retval !=0 On failure.
|
|
*/
|
|
static int log_syslog_close() {
|
|
if (log_syslog_enabled) {
|
|
log_syslog_enabled = false;
|
|
log_se->close();
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
Open POSIX syslog / Windows EventLog.
|
|
|
|
@retval -3 log already open, close it before opening again! (log still open)
|
|
@retval -2 cannot set up new registry entry, continuing with previous value
|
|
(log still open, but continues to log under previous key)
|
|
@retval -1 cannot set up new registry entry, no previous value
|
|
(log not opened)
|
|
@retval 0 success (log opened)
|
|
*/
|
|
int log_syslog_open() {
|
|
int ret;
|
|
const char *ident = (log_syslog_ident != nullptr) ? log_syslog_ident : PREFIX;
|
|
|
|
if (log_syslog_enabled) return -3;
|
|
|
|
ret = log_se->open(ident,
|
|
#ifndef _WIN32
|
|
log_syslog_include_pid ? MY_SYSLOG_PIDS : 0,
|
|
log_syslog_facility
|
|
#else
|
|
0, 0
|
|
#endif
|
|
);
|
|
|
|
if (ret != -1) log_syslog_enabled = true;
|
|
|
|
if (ret == -2) {
|
|
log_bi->message(/* purecov: inspected */
|
|
LOG_TYPE_ERROR, LOG_ITEM_LOG_PRIO, (longlong)ERROR_LEVEL,
|
|
LOG_ITEM_LOG_LOOKUP,
|
|
(longlong)ER_COULD_NOT_CREATE_WINDOWS_REGISTRY_KEY, MY_NAME,
|
|
ident, "logging");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
Syslog settings have changed; close and re-open.
|
|
*/
|
|
static void log_syslog_reopen() {
|
|
if (log_syslog_enabled) {
|
|
log_syslog_close();
|
|
log_syslog_open();
|
|
}
|
|
}
|
|
|
|
/**
|
|
Stop using syslog / EventLog. Call as late as possible.
|
|
*/
|
|
void log_syslog_exit(void) {
|
|
log_syslog_close();
|
|
|
|
// free ident.
|
|
if (log_syslog_ident != nullptr) {
|
|
log_bs->free(log_syslog_ident);
|
|
log_syslog_ident = nullptr;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Functions to validate and set variables governing the sys/event-logging
|
|
(tag/ident, facility, etc.). The following functions manipulate the
|
|
internal representation of these values. They are used by a layer
|
|
of callback functions run on changes of our component system variables.
|
|
*/
|
|
|
|
/**
|
|
Internal state: Validate new tag to log under
|
|
|
|
@param tag a string containing the tag
|
|
|
|
@retval 0 no complaints
|
|
@retval -1 no argument
|
|
@retval 1 invalid argument
|
|
*/
|
|
static int var_check_tag(const char *tag) {
|
|
if (tag == nullptr) return -1;
|
|
if (strchr(tag, '/') != nullptr) return 1;
|
|
if (strchr(tag, '\\') != nullptr) return 1;
|
|
if (log_bs->length(tag) >= MAX_TAG_LEN) return 1;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
Internal state: Update tag to log under
|
|
|
|
@param tag new tag to log under (will be appended to PREFIX)
|
|
|
|
@retval 0 success
|
|
@retval -1 EINVAL
|
|
@retval -2 out of memory
|
|
*/
|
|
static int var_update_tag(const char *tag) {
|
|
char *new_ident = nullptr, *old_ident = nullptr;
|
|
bool ident_changed = false;
|
|
|
|
// tag must not contain directory separators
|
|
if ((tag != nullptr) && (strchr(tag, FN_LIBCHAR) != nullptr)) return -1;
|
|
|
|
/*
|
|
make ident
|
|
*/
|
|
|
|
if ((tag == nullptr) || (*tag == '\0'))
|
|
new_ident = log_bs->strndup(PREFIX, log_bs->length(PREFIX));
|
|
else {
|
|
// prefix + '-' + '\0' + tag
|
|
size_t l = log_bs->length(PREFIX) + 1 + 1 + log_bs->length(tag);
|
|
|
|
new_ident = (char *)log_bs->malloc(l);
|
|
if (new_ident != nullptr)
|
|
log_bs->substitute(new_ident, l, "%s-%s", PREFIX, tag);
|
|
}
|
|
|
|
// if we succeeded in making an ident, replace the old one
|
|
if (new_ident != nullptr) {
|
|
ident_changed = (log_syslog_ident == nullptr) ||
|
|
(strcmp(new_ident, log_syslog_ident) != 0);
|
|
} else
|
|
return -2; /* purecov: inspected */
|
|
|
|
if (ident_changed) {
|
|
old_ident = log_syslog_ident;
|
|
log_syslog_ident = new_ident;
|
|
|
|
log_syslog_reopen();
|
|
|
|
if (old_ident != nullptr) log_bs->free(old_ident);
|
|
} else
|
|
log_bs->free(new_ident);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
/**
|
|
Internal state: Validate facility to log under
|
|
|
|
@param fac a string containing the facility
|
|
|
|
@retval 0 no complaints
|
|
@retval -1 unknown facility
|
|
@retval -2 facility name exceeds buffer
|
|
*/
|
|
static int var_check_fac(const char *fac) {
|
|
SYSLOG_FACILITY rsf;
|
|
|
|
if (log_syslog_find_facility(fac, &rsf))
|
|
return -1;
|
|
else if (log_bs->length(fac) >= MAX_FAC_LEN)
|
|
return -2; /* purecov: inspected */
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
Internal state: Update facility to log under
|
|
May change facility to its canonical representation if needed.
|
|
|
|
@param fac new facility to log under
|
|
|
|
@retval 0 success
|
|
*/
|
|
static int var_update_fac(char *fac) {
|
|
SYSLOG_FACILITY rsf = {LOG_DAEMON, LOG_DAEMON_NAME};
|
|
|
|
/*
|
|
make facility
|
|
*/
|
|
|
|
DBUG_ASSERT(fac != nullptr);
|
|
|
|
log_syslog_find_facility(fac, &rsf);
|
|
|
|
// If NaN, set to the canonical form (cut "log_", fix case)
|
|
if ((rsf.name != nullptr) && (strcmp(fac, rsf.name) != 0))
|
|
strcpy(fac, rsf.name);
|
|
|
|
// if the value has actually changed, tell the subsystem about it
|
|
if (log_syslog_facility != rsf.id) {
|
|
log_syslog_facility = rsf.id;
|
|
log_syslog_reopen();
|
|
}
|
|
|
|
// signal success (always, since we fail gracefully with a sensible default)
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
Internal state: Toggle inclusion of process ID (pid)
|
|
|
|
@param inc_pid include PID?
|
|
|
|
@retval 0 success
|
|
*/
|
|
static int var_update_pid(bool inc_pid) {
|
|
if (inc_pid != log_syslog_include_pid) {
|
|
log_syslog_include_pid = inc_pid;
|
|
log_syslog_reopen();
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
Component system variable handling.
|
|
Uses the above functions to manipulate internal state.
|
|
*/
|
|
|
|
/**
|
|
System-variable:
|
|
Check proposed value for component variable controlling tag to log under.
|
|
Queries internal state as needed.
|
|
|
|
@param thd session
|
|
@param self the system variable we're checking
|
|
@param save where to save the resulting intermediate (char *) value
|
|
@param value the value we're validating
|
|
|
|
@retval false value OK, go ahead and update system variable (from "save")
|
|
@retval true value rejected, do not update variable
|
|
*/
|
|
static int sysvar_check_tag(MYSQL_THD thd MY_ATTRIBUTE((unused)),
|
|
SYS_VAR *self MY_ATTRIBUTE((unused)), void *save,
|
|
struct st_mysql_value *value) {
|
|
int value_len = 0;
|
|
const char *proposed_value;
|
|
|
|
if (value == nullptr) return true;
|
|
|
|
proposed_value = value->val_str(value, nullptr, &value_len);
|
|
|
|
if (proposed_value == nullptr) return true;
|
|
|
|
DBUG_ASSERT(proposed_value[value_len] == '\0');
|
|
|
|
if (var_check_tag(proposed_value) != 0) // no complaints?
|
|
return true;
|
|
|
|
*static_cast<const char **>(save) = proposed_value;
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
System-variable:
|
|
Update value of component variable controlling tag to log under
|
|
Updates internal state as needed.
|
|
|
|
@param thd session
|
|
@param self the system variable we're changing
|
|
@param var_ptr where to save the resulting (char *) value
|
|
@param save pointer to the new value (from check function)
|
|
*/
|
|
static void sysvar_update_tag(MYSQL_THD thd MY_ATTRIBUTE((unused)),
|
|
SYS_VAR *self MY_ATTRIBUTE((unused)),
|
|
void *var_ptr, const void *save) {
|
|
const char *new_val = *(static_cast<const char **>(const_cast<void *>(save)));
|
|
|
|
var_update_tag(new_val);
|
|
|
|
if (var_ptr != nullptr) {
|
|
// the caller will free the old value, don't double free it here!
|
|
*((const char **)var_ptr) = new_val;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Set up system variable containing the tag to log under (for ident).
|
|
|
|
@retval 0 success
|
|
@retval -1 failure
|
|
*/
|
|
static int sysvar_install_tag(void) {
|
|
char *var_value;
|
|
char *new_value;
|
|
size_t var_len = MAX_TAG_LEN;
|
|
int rr = -1;
|
|
|
|
if ((var_value = new char[MAX_TAG_LEN + 1]) == nullptr) return -1;
|
|
|
|
values_tag.def_val = const_cast<char *>("");
|
|
|
|
DBUG_ASSERT(buffer_tag == nullptr);
|
|
|
|
if (mysql_service_component_sys_variable_register->register_variable(
|
|
MY_NAME, OPT_TAG, PLUGIN_VAR_STR | PLUGIN_VAR_MEMALLOC,
|
|
"When logging issues using the host operating system's " LOG_TYPE ", "
|
|
"tag the entries from this particular MySQL server with this ident. "
|
|
"This will help distinguish entries from MySQL servers co-existing "
|
|
"on the same host machine. A non-empty tag will be appended to the "
|
|
"default ident of '" PREFIX "', connected by a hyphen.",
|
|
sysvar_check_tag, sysvar_update_tag, (void *)&values_tag,
|
|
(void *)&buffer_tag) ||
|
|
mysql_service_component_sys_variable_register->get_variable(
|
|
MY_NAME, OPT_TAG, (void **)&var_value, &var_len))
|
|
goto done; /* purecov: inspected */
|
|
|
|
/*
|
|
We asked the server for a value for this variable as it may have
|
|
been SET PERSISTed.
|
|
*/
|
|
|
|
if ((rr = var_check_tag(var_value)))
|
|
log_bi->message(LOG_TYPE_ERROR, LOG_ITEM_LOG_PRIO, (longlong)WARNING_LEVEL,
|
|
LOG_ITEM_LOG_LOOKUP,
|
|
(longlong)ER_SERVER_WRONG_VALUE_FOR_VAR,
|
|
MY_NAME "." OPT_TAG, var_value);
|
|
|
|
/*
|
|
If the actual setup worked, but we were passed an invalid value
|
|
for the variable, try to set default!
|
|
*/
|
|
|
|
new_value = (rr == 0) ? var_value : values_tag.def_val;
|
|
|
|
if (!var_update_tag(new_value)) {
|
|
/*
|
|
Update of internal state succeeded!
|
|
Update system variable's value if adjusted.
|
|
*/
|
|
char *old = buffer_tag;
|
|
if ((buffer_tag = log_bs->strndup(
|
|
new_value, log_bs->length(new_value) + 1)) != nullptr) {
|
|
if (old != nullptr) log_bs->free((void *)old); /* purecov: inspected */
|
|
rr = 0;
|
|
goto done;
|
|
}
|
|
|
|
// if we failed to copy the default, restore the previous value
|
|
buffer_tag = old; /* purecov: inspected */
|
|
}
|
|
|
|
rr = -1; /* purecov: inspected */
|
|
|
|
done:
|
|
delete[] var_value;
|
|
return rr;
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
|
|
/**
|
|
System-variable:
|
|
Check proposed value for component variable controlling facility to log under.
|
|
Queries internal state as needed.
|
|
|
|
@param thd session
|
|
@param self the system variable we're checking
|
|
@param save where to save the resulting intermediate (char *) value
|
|
@param value the value we're validating
|
|
|
|
@retval false value OK, go ahead and update system variable (from "save")
|
|
@retval true value rejected, do not update variable
|
|
*/
|
|
static int sysvar_check_fac(MYSQL_THD thd MY_ATTRIBUTE((unused)),
|
|
SYS_VAR *self MY_ATTRIBUTE((unused)), void *save,
|
|
struct st_mysql_value *value) {
|
|
int value_len = 0;
|
|
const char *proposed_value;
|
|
|
|
if (value == nullptr) return true;
|
|
|
|
proposed_value = value->val_str(value, nullptr, &value_len);
|
|
|
|
if (proposed_value == nullptr) return true;
|
|
|
|
DBUG_ASSERT(proposed_value[value_len] == '\0');
|
|
|
|
if (var_check_fac(proposed_value) != 0) // if value is invalid, bail
|
|
return true;
|
|
|
|
*static_cast<const char **>(save) = proposed_value;
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
System-variable:
|
|
Update value of component variable controlling facilty to log under
|
|
Updates internal state as needed.
|
|
|
|
@param thd session
|
|
@param self the system variable we're changing
|
|
@param var_ptr where to save the resulting (char *) value
|
|
@param save pointer to the new value (from check function)
|
|
*/
|
|
static void sysvar_update_fac(MYSQL_THD thd MY_ATTRIBUTE((unused)),
|
|
SYS_VAR *self MY_ATTRIBUTE((unused)),
|
|
void *var_ptr, const void *save) {
|
|
char *new_val = *(static_cast<char **>(const_cast<void *>(save)));
|
|
|
|
var_update_fac(new_val);
|
|
|
|
if (var_ptr != nullptr) {
|
|
// the caller will free the old value, don't double free it here!
|
|
*((char **)var_ptr) = new_val;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Set up system variable containing the tag to log under (for ident).
|
|
|
|
@retval 0 success
|
|
@retval -1 failure
|
|
*/
|
|
static int sysvar_install_fac(void) {
|
|
char *var_value;
|
|
char *new_value;
|
|
size_t var_len = MAX_FAC_LEN;
|
|
int rr = -1;
|
|
|
|
if ((var_value = new char[MAX_FAC_LEN + 1]) == nullptr) return -1;
|
|
|
|
values_fac.def_val = const_cast<char *>(LOG_DAEMON_NAME);
|
|
|
|
if (mysql_service_component_sys_variable_register->register_variable(
|
|
MY_NAME, OPT_FAC, PLUGIN_VAR_STR | PLUGIN_VAR_MEMALLOC,
|
|
"When logging issues using the host operating system's syslog, "
|
|
"identify as a facility of the given type (to aid in log filtering).",
|
|
sysvar_check_fac, sysvar_update_fac, (void *)&values_fac,
|
|
(void *)&buffer_fac) ||
|
|
mysql_service_component_sys_variable_register->get_variable(
|
|
MY_NAME, OPT_FAC, (void **)&var_value, &var_len))
|
|
goto done; /* purecov: inspected */
|
|
|
|
/*
|
|
We asked the server for a value for this variable as it may have
|
|
been SET PERSISTed.
|
|
*/
|
|
|
|
if ((rr = var_check_fac(var_value)))
|
|
log_bi->message(LOG_TYPE_ERROR, LOG_ITEM_LOG_PRIO, (longlong)WARNING_LEVEL,
|
|
LOG_ITEM_LOG_LOOKUP,
|
|
(longlong)ER_SERVER_WRONG_VALUE_FOR_VAR,
|
|
MY_NAME "." OPT_FAC, var_value);
|
|
|
|
/*
|
|
If the actual setup worked, but we were passed an invalid value
|
|
for the variable, try to set default!
|
|
*/
|
|
|
|
new_value = (rr == 0) ? var_value : values_fac.def_val;
|
|
|
|
var_update_fac(new_value);
|
|
|
|
/*
|
|
Update of internal state succeeded!
|
|
Update system variable's value if adjusted.
|
|
*/
|
|
if (rr != 0) {
|
|
char *old = buffer_fac;
|
|
if ((buffer_fac = log_bs->strndup(
|
|
new_value, log_bs->length(new_value) + 1)) != nullptr) {
|
|
if (old != nullptr) log_bs->free((void *)old);
|
|
rr = 0;
|
|
goto done;
|
|
}
|
|
|
|
// if we failed to copy the default, restore the previous value
|
|
buffer_fac = old; /* purecov: inspected */
|
|
rr = -1; /* purecov: inspected */
|
|
}
|
|
|
|
done:
|
|
delete[] var_value;
|
|
return rr;
|
|
}
|
|
|
|
/**
|
|
System-variable:
|
|
Update value of component variable governing inclusion of process ID (pid)
|
|
Updates internal state as needed.
|
|
|
|
@param thd session
|
|
@param self the system variable we're changing
|
|
@param var_ptr where to save the resulting (char *) value
|
|
@param save pointer to the new value (from check function)
|
|
*/
|
|
static void sysvar_update_pid(MYSQL_THD thd MY_ATTRIBUTE((unused)),
|
|
SYS_VAR *self MY_ATTRIBUTE((unused)),
|
|
void *var_ptr MY_ATTRIBUTE((unused)),
|
|
const void *save) {
|
|
var_update_pid(*(static_cast<bool *>(const_cast<void *>(save))));
|
|
}
|
|
|
|
/*
|
|
Set up system variable governing the inclusion of the process ID (pid).
|
|
|
|
@retval 0 success
|
|
@retval -1 failure
|
|
*/
|
|
static int sysvar_install_pid(void) {
|
|
char *var_value = nullptr;
|
|
size_t var_len = 15;
|
|
bool var_bool;
|
|
int rr = -1;
|
|
|
|
values_pid.def_val = log_syslog_include_pid;
|
|
|
|
if ((var_value = new char[var_len + 1]) == nullptr) return -1;
|
|
|
|
// register variable
|
|
if (mysql_service_component_sys_variable_register->register_variable(
|
|
MY_NAME, OPT_PID, PLUGIN_VAR_BOOL,
|
|
"When logging issues using the host operating system's log "
|
|
"(\"" LOG_TYPE "\"), include this MySQL server's process ID (PID). "
|
|
"This setting does not affect MySQL's own error log file.",
|
|
nullptr, sysvar_update_pid, (void *)&values_pid,
|
|
(void *)&log_syslog_include_pid) ||
|
|
|
|
// get variable in case it was PERSISTed
|
|
mysql_service_component_sys_variable_register->get_variable(
|
|
MY_NAME, OPT_PID, (void **)&var_value, &var_len))
|
|
goto done; /* purecov: inspected */
|
|
|
|
// set the (possibly PERSISTed) value we received from the server
|
|
var_bool = ((native_strcasecmp(var_value, "ON") == 0));
|
|
rr = var_update_pid(var_bool);
|
|
|
|
done:
|
|
delete[] var_value;
|
|
return rr;
|
|
}
|
|
|
|
#endif
|
|
|
|
/**
|
|
services: log sinks: classic syslog/EventLog writer (message only)
|
|
label will be ignored (one will be generated from priority by the syslogger).
|
|
If the message is not \0 terminated, it will be terminated.
|
|
|
|
@param instance instance's state
|
|
@param ll the log line to write
|
|
|
|
@retval >=0 number of accepted fields, if any
|
|
@retval -1 log was not open
|
|
@retval -2 could not sanitize log message
|
|
@retval -3 failure not otherwise specified
|
|
*/
|
|
DEFINE_METHOD(int, log_service_imp::run,
|
|
(void *instance MY_ATTRIBUTE((unused)), log_line *ll)) {
|
|
const char *msg = nullptr;
|
|
int out_fields = 0;
|
|
enum loglevel level = ERROR_LEVEL;
|
|
log_item_type item_type = LOG_ITEM_END;
|
|
log_item_type_mask out_types = 0;
|
|
|
|
log_item_iter *it;
|
|
log_item *li;
|
|
|
|
if (!log_syslog_enabled) return -1;
|
|
|
|
if ((it = log_bi->line_item_iter_acquire(ll)) == nullptr) return -3;
|
|
|
|
li = log_bi->line_item_iter_first(it);
|
|
|
|
while (li != nullptr) {
|
|
item_type = li->type;
|
|
|
|
if (log_bi->item_inconsistent(li)) goto skip_item;
|
|
|
|
if (item_type == LOG_ITEM_LOG_PRIO)
|
|
level = static_cast<enum loglevel>(li->data.data_integer);
|
|
else if (item_type == LOG_ITEM_LOG_MESSAGE) {
|
|
if (log_bi->sanitize(li) < 0) {
|
|
log_bi->line_item_iter_release(it);
|
|
return -2;
|
|
}
|
|
|
|
msg = li->data.data_string.str;
|
|
} else if (item_type != LOG_ITEM_LOG_LABEL)
|
|
// This backend won't let us use custom labels, so we skip over them.
|
|
goto skip_item;
|
|
|
|
out_types |= item_type;
|
|
out_fields++;
|
|
|
|
skip_item:
|
|
li = log_bi->line_item_iter_next(it);
|
|
}
|
|
|
|
if ((out_types & (LOG_ITEM_LOG_PRIO | LOG_ITEM_LOG_MESSAGE)) ==
|
|
(LOG_ITEM_LOG_PRIO | LOG_ITEM_LOG_MESSAGE)) {
|
|
log_se->write(level, msg);
|
|
}
|
|
|
|
log_bi->line_item_iter_release(it);
|
|
|
|
return out_fields;
|
|
}
|
|
|
|
/**
|
|
De-initialization method for Component used when unloading the Component.
|
|
|
|
@return Status of performed operation
|
|
@retval false success
|
|
@retval true failure
|
|
*/
|
|
mysql_service_status_t log_service_exit() {
|
|
if (inited) {
|
|
log_syslog_exit();
|
|
|
|
// release system variables if we hold any
|
|
#ifndef _WIN32
|
|
mysql_service_component_sys_variable_unregister->unregister_variable(
|
|
MY_NAME, OPT_PID);
|
|
mysql_service_component_sys_variable_unregister->unregister_variable(
|
|
MY_NAME, OPT_FAC);
|
|
#endif
|
|
mysql_service_component_sys_variable_unregister->unregister_variable(
|
|
MY_NAME, OPT_TAG);
|
|
|
|
log_bi = nullptr;
|
|
log_bs = nullptr;
|
|
log_se = nullptr;
|
|
#ifdef _WIN32
|
|
log_bt = nullptr;
|
|
#endif
|
|
|
|
buffer_tag = nullptr;
|
|
buffer_fac = nullptr;
|
|
|
|
inited = false;
|
|
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
Initialization entry method for Component used when loading the Component.
|
|
|
|
@return Status of performed operation
|
|
@retval false success
|
|
@retval true failure
|
|
*/
|
|
mysql_service_status_t log_service_init() {
|
|
if (inited) return true;
|
|
|
|
inited = true;
|
|
|
|
log_bi = mysql_service_log_builtins;
|
|
log_bs = mysql_service_log_builtins_string;
|
|
log_se = mysql_service_log_builtins_syseventlog;
|
|
#ifdef _WIN32
|
|
log_bt = mysql_service_log_builtins_tmp;
|
|
#endif
|
|
|
|
/*
|
|
Register our system variables.
|
|
Enable last, so all the values are correct by the time we open
|
|
the log (which is especially useful when using a non-default tag
|
|
on Windows as it prevents us from first creating a tag in the
|
|
registry that we then won't use).
|
|
*/
|
|
|
|
/* try to set up system-variables */
|
|
if (sysvar_install_tag()
|
|
#ifndef _WIN32
|
|
|| sysvar_install_fac() || sysvar_install_pid()
|
|
#endif
|
|
)
|
|
goto fail; /* purecov: inspected */
|
|
|
|
/*
|
|
If this component is loaded, we enable it by default, as that's
|
|
probably what the user expects.
|
|
*/
|
|
log_syslog_open();
|
|
if (!log_syslog_enabled) goto fail;
|
|
|
|
return false;
|
|
|
|
/*
|
|
If we failed to open our log, try to log the failure to any open loggers.
|
|
*/
|
|
|
|
fail:
|
|
/* purecov: begin inspected */
|
|
log_bi->message(LOG_TYPE_ERROR, LOG_ITEM_LOG_PRIO, (longlong)ERROR_LEVEL,
|
|
LOG_ITEM_LOG_LOOKUP, (longlong)ER_LOG_SYSLOG_CANNOT_OPEN,
|
|
LOG_TYPE);
|
|
|
|
log_service_exit();
|
|
return true; /* purecov: end */
|
|
}
|
|
|
|
/* flush logs */
|
|
DEFINE_METHOD(int, log_service_imp::flush,
|
|
(void **instance MY_ATTRIBUTE((unused)))) {
|
|
if (inited) log_service_exit();
|
|
return log_service_init();
|
|
}
|
|
|
|
/**
|
|
Open a new instance.
|
|
|
|
@param ll optional arguments
|
|
@param instance If state is needed, the service may allocate and
|
|
initialize it and return a pointer to it here.
|
|
(This of course is particularly pertinent to
|
|
components that may be opened multiple times,
|
|
such as the JSON log writer.)
|
|
This state is for use of the log-service component
|
|
in question only and can take any layout suitable
|
|
to that component's need. The state is opaque to
|
|
the server/logging framework. It must be released
|
|
on close.
|
|
|
|
@retval <0 a new instance could not be created
|
|
@retval =0 success, returned hande is valid
|
|
*/
|
|
DEFINE_METHOD(int, log_service_imp::open,
|
|
(log_line * ll MY_ATTRIBUTE((unused)), void **instance)) {
|
|
if (instance == nullptr) return -1;
|
|
|
|
*instance = nullptr;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
Close and release an instance. Flushes any buffers.
|
|
|
|
@param instance State-pointer that was returned on open.
|
|
If memory was allocated for this state,
|
|
it should be released, and the pointer
|
|
set to nullptr.
|
|
|
|
@retval <0 an error occurred
|
|
@retval =0 success
|
|
*/
|
|
DEFINE_METHOD(int, log_service_imp::close,
|
|
(void **instance MY_ATTRIBUTE((unused)))) {
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
Get characteristics of a log-service.
|
|
|
|
@retval <0 an error occurred
|
|
@retval >=0 characteristics (a set of log_service_chistics flags)
|
|
*/
|
|
DEFINE_METHOD(int, log_service_imp::characteristics, (void)) {
|
|
return LOG_SERVICE_SINK | LOG_SERVICE_SINGLETON;
|
|
}
|
|
|
|
/* implementing a service: log_service */
|
|
BEGIN_SERVICE_IMPLEMENTATION(log_sink_syseventlog, log_service)
|
|
log_service_imp::run, log_service_imp::flush, log_service_imp::open,
|
|
log_service_imp::close,
|
|
log_service_imp::characteristics END_SERVICE_IMPLEMENTATION();
|
|
|
|
/* component provides: just the log_service service, for now */
|
|
BEGIN_COMPONENT_PROVIDES(log_sink_syseventlog)
|
|
PROVIDES_SERVICE(log_sink_syseventlog, log_service), END_COMPONENT_PROVIDES();
|
|
|
|
/* component requires: log-builtins */
|
|
BEGIN_COMPONENT_REQUIRES(log_sink_syseventlog)
|
|
REQUIRES_SERVICE(component_sys_variable_register),
|
|
REQUIRES_SERVICE(component_sys_variable_unregister),
|
|
REQUIRES_SERVICE(log_builtins), REQUIRES_SERVICE(log_builtins_string),
|
|
REQUIRES_SERVICE(log_builtins_syseventlog),
|
|
#ifdef _WIN32
|
|
REQUIRES_SERVICE(log_builtins_tmp),
|
|
#endif
|
|
END_COMPONENT_REQUIRES();
|
|
|
|
/* component description */
|
|
BEGIN_COMPONENT_METADATA(log_sink_syseventlog)
|
|
METADATA("mysql.author", "T.A. Nuernberg, Oracle Corporation"),
|
|
METADATA("mysql.license", "GPL"), METADATA("log_service_type", "sink"),
|
|
END_COMPONENT_METADATA();
|
|
|
|
/* component declaration */
|
|
DECLARE_COMPONENT(log_sink_syseventlog, "mysql:log_sink_syseventlog")
|
|
log_service_init, log_service_exit END_DECLARE_COMPONENT();
|
|
|
|
/* components contained in this library.
|
|
for now assume that each library will have exactly one component. */
|
|
DECLARE_LIBRARY_COMPONENTS &COMPONENT_REF(log_sink_syseventlog)
|
|
END_DECLARE_LIBRARY_COMPONENTS
|
|
|
|
/* EOT */
|