1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
|
/*
Copyright (c) 2000, 2025, Oracle and/or its affiliates.
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 designed to work 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 either included with
the program or referenced in the documentation.
Without limiting anything contained in the foregoing, this file,
which is part of C Driver for MySQL (Connector/C), is also subject to the
Universal FOSS Exception, version 1.0, a copy of which can be found at
http://oss.oracle.com/licenses/universal-foss-exception.
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
*/
/**
@file mysys/my_syslog.cc
*/
#include <stddef.h>
#include "m_ctype.h"
#include "my_compiler.h"
#include "my_dbug.h"
#include "my_loglevel.h"
#include "my_sys.h"
#if defined(_WIN32)
#include <stdio.h>
#include "mysql/service_mysql_alloc.h"
#include "mysys/mysys_priv.h"
#endif
extern CHARSET_INFO my_charset_utf16le_bin;
#ifndef _WIN32
#include <syslog.h>
#endif
#ifdef _WIN32
/**
Windows EventLog messages
See sql/message.mc for the defined messages on Windows
(and on how to add/modify messages).
Presently, only one message is used: message 100 (MSG_DEFAULT)
is a template "%1" into which we insert the message my_syslog()
is given.
0x[C][000][0064] -- 32 bits, left to right:
0xC:
2:Severity (0x3 == Error) (0xC>>2)
1:Custom or System (0x0 == System) ((0xC>>1) & 1)
1:Rsrvd (0x0) (0xC & 1)
0x000:
12:Facility (0x000 == FACILITY_NULL)
0x0064
16:Code (0x0064 == 100)
*/
#define MSG_DEFAULT 0xC0000064L
static HANDLE hEventLog = NULL; // global
#endif
/**
Sends message to the system logger. On Windows, the specified message is
internally converted to UCS-2 encoding, while on other platforms, no
conversion takes place and the string is passed to the syslog API as it is.
@param cs Character set info of the message string
@param level Log level
@param msg Message to be logged (C-string)
@return
0 Success
-1 Error
*/
int my_syslog(const CHARSET_INFO *cs [[maybe_unused]], enum loglevel level,
const char *msg) {
#ifdef _WIN32
int _level = EVENTLOG_INFORMATION_TYPE;
wchar_t buff[MAX_SYSLOG_MESSAGE_SIZE];
wchar_t *u16buf = NULL;
size_t msg_len; // bytes (not wide-chars) in input
size_t nbytes; // bytes (not wide-chars) in output
uint dummy_errors; // number of conversion errors
DBUG_TRACE;
if ((msg == nullptr) || ((msg_len = strlen(msg)) == 0)) return 0;
// limit input to buffer size
if (msg_len >= MAX_SYSLOG_MESSAGE_SIZE) msg_len = MAX_SYSLOG_MESSAGE_SIZE - 1;
switch (level) {
case SYSTEM_LEVEL:
// fall-through. consider EVENTLOG_SUCCESS.
case INFORMATION_LEVEL:
_level = EVENTLOG_INFORMATION_TYPE;
break;
case WARNING_LEVEL:
_level = EVENTLOG_WARNING_TYPE;
break;
case ERROR_LEVEL:
_level = EVENTLOG_ERROR_TYPE;
break;
default:
assert(false);
}
if (hEventLog) {
nbytes =
my_convert((char *)buff, sizeof(buff) - sizeof(buff[0]),
&my_charset_utf16le_bin, msg, msg_len, cs, &dummy_errors);
// terminate it with NULL
buff[nbytes / sizeof(wchar_t)] = L'\0';
u16buf = buff;
// Fetch mysqld's Windows default message, insert u16buf, log the result.
if (!ReportEventW(hEventLog,
_level, // severity
0, // app-defined event category
MSG_DEFAULT, // event ID / message ID (see above)
NULL, // security identifier
1, // number of strings in u16buf
0, // number of bytes in raw data
const_cast<LPCWSTR *>(&u16buf), // 0-terminated strings
NULL)) // raw (binary data)
goto err;
}
// Message successfully written to the event log.
return 0;
err:
// map error appropriately
my_osmaperr(GetLastError());
return -1;
#else
int _level = LOG_INFO;
DBUG_TRACE;
if ((msg == nullptr) || (*msg == '\0')) return 0;
switch (level) {
case INFORMATION_LEVEL:
case SYSTEM_LEVEL:
_level = LOG_INFO;
break;
case WARNING_LEVEL:
_level = LOG_WARNING;
break;
case ERROR_LEVEL:
_level = LOG_ERR;
break;
default:
assert(false);
}
syslog(_level, "%s", msg);
return 0;
#endif /* _WIN32 */
}
#ifdef _WIN32
/**
Create a key in the Windows registry.
We'll setup a "MySQL" key in the EventLog branch (RegCreateKey),
set our executable name (GetModuleFileName) as file-name
("EventMessageFile"), then set the message types we expect to
be logging ("TypesSupported").
If the key does not exist, sufficient privileges will be required
to create and configure it. If the key does exist, opening it
should be unprivileged; modifying will fail on insufficient
privileges, but that is non-fatal.
@param key Name of the event generator.
(Only last part of the key, e.g. "MySQL")
@return
0 Success
-1 Error
*/
const char registry_prefix[] =
"SYSTEM\\CurrentControlSet\\services\\eventlog\\Application\\";
static int windows_eventlog_create_registry_entry(const char *key) {
HKEY hRegKey = NULL;
DWORD dwError = 0;
TCHAR szPath[MAX_PATH];
DWORD dwTypes;
size_t l = sizeof(registry_prefix) + strlen(key) + 1;
char *buff;
int ret = 0;
DBUG_TRACE;
if ((buff = (char *)my_malloc(PSI_NOT_INSTRUMENTED, l, MYF(0))) == NULL)
return -1;
snprintf(buff, l, "%s%s", registry_prefix, key);
// Opens the event source registry key; creates it first if required.
dwError = RegCreateKey(HKEY_LOCAL_MACHINE, buff, &hRegKey);
my_free(buff);
if (dwError != ERROR_SUCCESS) {
if (dwError == ERROR_ACCESS_DENIED) {
my_message_stderr(0,
"Could not create or access the registry key needed "
"for the MySQL application\n"
"to log to the Windows EventLog. Run the application "
"with sufficient\n"
"privileges once to create the key, add the key "
"manually, or turn off\n"
"logging for that application.",
MYF(0));
}
return -1;
}
/* Name of the PE module that contains the message resource */
GetModuleFileName(NULL, szPath, MAX_PATH);
/* Register EventMessageFile (DLL/exec containing event identifiers) */
dwError = RegSetValueEx(hRegKey, "EventMessageFile", 0, REG_EXPAND_SZ,
(PBYTE)szPath, (DWORD)(strlen(szPath) + 1));
if ((dwError != ERROR_SUCCESS) && (dwError != ERROR_ACCESS_DENIED)) ret = -1;
/* Register supported event types */
dwTypes =
(EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE);
dwError = RegSetValueEx(hRegKey, "TypesSupported", 0, REG_DWORD,
(LPBYTE)&dwTypes, sizeof dwTypes);
if ((dwError != ERROR_SUCCESS) && (dwError != ERROR_ACCESS_DENIED)) ret = -1;
RegCloseKey(hRegKey);
return ret;
}
#endif
/**
Opens/Registers a new handle for system logging.
Note: It's a thread-unsafe function. It should either
be invoked from the main thread or some extra thread
safety measures need to be taken.
@param name Name of the event source / syslog ident.
@param option MY_SYSLOG_PIDS to log PID with each message.
@param facility Type of program. Passed to openlog().
@return
0 Success
-1 Error, log not opened
-2 Error, not updated, using previous values
*/
int my_openlog(const char *name, int option, int facility) {
#ifndef _WIN32
int opts = (option & MY_SYSLOG_PIDS) ? LOG_PID : 0;
DBUG_TRACE;
openlog(name, opts | LOG_NDELAY, facility);
#else
(void)option; // maybe unused
(void)facility; // maybe unused
HANDLE hEL_new;
DBUG_TRACE;
// OOM failsafe. Not needed for syslog.
if (name == NULL) return -1;
if ((windows_eventlog_create_registry_entry(name) != 0) ||
!(hEL_new = RegisterEventSource(NULL, name))) {
// map error appropriately
my_osmaperr(GetLastError());
return (hEventLog == NULL) ? -1 : -2;
} else {
if (hEventLog != NULL) DeregisterEventSource(hEventLog);
hEventLog = hEL_new;
}
#endif
return 0;
}
/**
Closes/de-registers the system logging handle.
Note: Its a thread-unsafe function. It should
either be invoked from the main thread or some
extra thread safety measures need to be taken.
@return
0 Success
-1 Error
*/
int my_closelog(void) {
DBUG_TRACE;
#ifndef _WIN32
closelog();
return 0;
#else
if ((hEventLog != NULL) && (!DeregisterEventSource(hEventLog))) goto err;
hEventLog = NULL;
return 0;
err:
hEventLog = NULL;
// map error appropriately
my_osmaperr(GetLastError());
return -1;
#endif
}
|