1 /* Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
2 
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6 
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation.  The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License, version 2.0, for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
22 
23 /*
24   NB  This module has an unusual amount of failsafes, OOM checks, and
25       so on as it implements a public API. This makes a fair number
26       of minor code paths cases of "we should never get here (unless
27       someone's going out of their way to break to API)". :)
28 */
29 
30 #define LOG_SUBSYSTEM_TAG "Server"
31 
32 #include "log_builtins_filter_imp.h"
33 #include "log_builtins_imp.h"  // internal structs
34                                // connection_events_loop_aborted()
35 
36 #include "my_dir.h"
37 #include "mysys_err.h"
38 
39 #include <mysql/components/services/log_service.h>
40 #include <mysql/components/services/log_shared.h>  // data types
41 
42 #include "sql/current_thd.h"  // current_thd
43 #include "sql/log.h"          // make_iso8601_timestamp, log_write_errstream,
44                               // log_get_thread_id, mysql_errno_to_symbol,
45                               // mysql_symbol_to_errno, log_vmessage,
46                               // error_message_for_error_log
47 #include "sql/mysqld.h"       // opt_log_(timestamps|error_services),
48 #include "sql/sql_class.h"    // THD
49 
50 // Must come after sql/log.h.
51 #include "mysql/components/services/log_builtins.h"
52 
53 #ifndef _WIN32
54 #include <syslog.h>
55 #else
56 #include <stdio.h>
57 
58 #include "my_sys.h"
59 
60 extern CHARSET_INFO my_charset_utf16le_bin;  // used in Windows EventLog
61 static HANDLE hEventLog = NULL;              // global
62 #define MSG_DEFAULT 0xC0000064L
63 #endif
64 
65 extern log_filter_ruleset *log_filter_builtin_rules;  // what it says on the tin
66 
67 PSI_memory_key key_memory_log_error_loaded_services;
68 PSI_memory_key key_memory_log_error_stack;
69 
70 using std::string;
71 using std::unique_ptr;
72 
73 struct log_service_cache_entry;
74 
75 struct log_service_cache_entry_free {
76   /**
77     Release an entry in the hash of log services.
78 
79     @param       sce     the entry to free
80   */
81   void operator()(log_service_cache_entry *sce) const;
82 };
83 
84 /**
85   We're caching handles to the services used in error logging
86   as looking them up is costly.
87 */
88 using cache_entry_with_deleter =
89     unique_ptr<log_service_cache_entry, log_service_cache_entry_free>;
90 static collation_unordered_map<string, cache_entry_with_deleter>
91     *log_service_cache;
92 
93 /**
94   Lock for the log "stack" (i.e. the list of active log-services).
95   X-locked while stack is changed/configured.
96   S-locked while stack is used.
97 */
98 static mysql_rwlock_t THR_LOCK_log_stack;
99 
100 /**
101   Make sure only one instance of syslog/Eventlog code runs at a time.
102 */
103 static mysql_mutex_t THR_LOCK_log_syseventlog;
104 
105 /**
106   Make sure only one instance of the buffered "writer" runs at a time.
107 
108   In normal operation, the log-event will be created dynamically, then
109   it will be fed through the pipeline, and then it will be released.
110   Since the event is allocated in the caller, we can be sure it won't
111   go away wholesale during processing, and since the event is local to
112   the caller, no other thread will tangle with it. It is therefore safe
113   in those cases not to wrap a lock around the event.
114   (The log-pipeline will still grab a shared lock, THR_LOCK_log_stack,
115   to protect the pipeline (not the event) and the log-services cache from
116   being changed while the pipeline is being applied.
117   Likewise, log-services may protect their resources (file-writers will
118   usually take a lock to serialize their writes; the built-in filter will
119   take a lock on its rule-set as that is shared between concurrent
120   threads running the filter, and so on).
121   None of these are intended to protect the event itself though.
122 
123   In buffered mode on the other hand, we copy each log-event (the
124   original of which, see above, is owned by the caller and local
125   to the thread, and therefore safe without locking) to a global
126   buffer / backlog. As this backlog can be added to by all threads,
127   it must be protected by a lock (once we have fully initialized
128   the subsystem with log_builtins_init() and support multi-threaded
129   mode anyway, as indicated by log_builtins_inited being true, see
130   below). This is that lock.
131 */
132 static mysql_mutex_t THR_LOCK_log_buffered;
133 
134 /**
135   Subsystem initialized and ready to use?
136 */
137 static bool log_builtins_inited = false;
138 
139 /**
140   Name of the interface that log-services implements.
141 */
142 #define LOG_SERVICES_PREFIX "log_service"
143 
144 struct log_line_buffer {
145   log_line ll;            ///< log-event we're buffering
146   log_line_buffer *next;  ///< chronologically next log-event
147 };
148 /// Pointer to the first element in the list of buffered log messages
149 static log_line_buffer *log_line_buffer_start = nullptr;
150 /// Where to write the pointer to the newly-created tail-element of the list
151 static log_line_buffer **log_line_buffer_tail = &log_line_buffer_start;
152 
153 /**
154   Timestamp: During buffered logging, when should we next consider flushing?
155   This variable is set to the time we'll next check whether we'll want to
156   flush buffered log events.
157   I.e. it is set to a future time (current time + window length); once
158   the value expires (turns into present/past), we check whether we need
159   to flush and update the variable to the next time we should check.
160 */
161 static ulonglong log_buffering_timeout = 0;
162 
163 /// If after this many seconds we're still buffering, flush!
164 #ifndef LOG_BUFFERING_TIMEOUT_AFTER
165 #define LOG_BUFFERING_TIMEOUT_AFTER (60)
166 #endif
167 /// Thereafter, if still buffering after this many more seconds, flush again!
168 #ifndef LOG_BUFFERING_TIMEOUT_EVERY
169 #define LOG_BUFFERING_TIMEOUT_EVERY (10)
170 #endif
171 /// Time function returns microseconds, we want seconds.
172 #ifndef LOG_BUFFERING_TIME_SCALE
173 #define LOG_BUFFERING_TIME_SCALE 1000000
174 #endif
175 
176 /// Does buffer contain SYSTEM or ERROR prio messages? Flush early only then!
177 static int log_buffering_flushworthy = false;
178 
179 /**
180   Timestamp of when we last flushed to traditional error-log
181   (built-in sink). Log lines with timestamps older than this
182   have already been flushed to the default log-sink, so we
183   won't do so again to prevent duplicates. (The buffered events
184   are still kept around until buffered logging ends however in
185   case we need to flush them to other log-writers then.
186   This timestamp is updated to the present and then drifts into
187   the past, in contrast to the time-out value (see there).
188 */
189 static ulonglong log_sink_trad_last = 0;
190 
191 /**
192   Timestamp of the last event we put into the error-log buffer
193   during buffered mode (while starting up). New items must
194   receive a LOG_ITEM_LOG_BUFFERED timestamp greater than this.
195 */
196 static ulonglong log_sink_buffer_last = 0;
197 
198 /**
199   Finding and acquiring a service in the component framework is
200   expensive, and we may use services a log (depending on how many
201   events are logged per second), so we cache the relevant data.
202   This struct describes a given service.
203 */
204 struct log_service_cache_entry {
205   char *name;            ///< name of this service
206   size_t name_len;       ///< service-name's length
207   my_h_service service;  ///< handle (service framework)
208   int opened;            ///< currently open instances
209   int requested;         ///< requested instances
210   int chistics;          ///< multi-open supported, etc.
211 };
212 
213 /**
214   State of a given instance of a service. A service may support being
215   opened several times.
216 */
217 typedef struct _log_service_instance {
218   log_service_cache_entry *sce;        ///< the service in question
219   void *instance;                      ///< instance handle (multi-open)
220   struct _log_service_instance *next;  ///< next instance (any service)
221 } log_service_instance;
222 
223 static log_service_instance *log_service_instances = nullptr;  ///< anchor
224 
225 /**
226   An error-stream.
227   Rather than implement its own file operations, a log-service may use
228   convenience functions defined in this file. These functions use the
229   log_errstream struct to describe their log-files. These structs are
230   opaque to the log-services.
231 */
232 struct log_errstream {
233   FILE *file{nullptr};           ///< file to log to
234   mysql_mutex_t LOCK_errstream;  ///< lock for logging
235 };
236 
237 /// What mode is error-logging in (e.g. are loadable services available yet)?
238 static enum log_error_stage log_error_stage_current =
239     LOG_ERROR_STAGE_BUFFERING_EARLY;
240 
241 /// Set error-logging stage hint (e.g. are loadable services available yet?).
log_error_stage_set(enum log_error_stage les)242 void log_error_stage_set(enum log_error_stage les) {
243   log_error_stage_current = les;
244 }
245 
246 /// What mode is error-logging in (e.g. are loadable services available yet)?
log_error_stage_get()247 enum log_error_stage log_error_stage_get() { return log_error_stage_current; }
248 
249 /**
250   Test whether a given log-service name refers to a built-in
251   service (built-in filter or built-in sink at this point).
252 
253   @param  name   the name -- either just the component's, or
254                  a fully qualified service.component
255   @param  len    the length of the aforementioned name
256   @retval        if built-in filter: flags for built-in|singleton|filter
257   @retval        if built-in sink:   flags for built-in|singleton|sink
258   @retval        otherwise:          LOG_SERVICE_UNSPECIFIED
259 */
log_service_check_if_builtin(const char * name,size_t len)260 static int log_service_check_if_builtin(const char *name, size_t len) {
261   const size_t builtin_len = sizeof(LOG_SERVICES_PREFIX) - 1;
262 
263   if ((len > (builtin_len + 1)) && (name[builtin_len] == '.') &&
264       (0 == strncmp(name, LOG_SERVICES_PREFIX, builtin_len))) {
265     name += builtin_len;
266     len -= builtin_len;
267   }
268 
269   if ((len == sizeof(LOG_BUILTINS_FILTER) - 1) &&
270       (0 == strncmp(name, LOG_BUILTINS_FILTER, len)))
271     return LOG_SERVICE_BUILTIN | LOG_SERVICE_FILTER | LOG_SERVICE_SINGLETON;
272 
273   if ((len == sizeof(LOG_BUILTINS_SINK) - 1) &&
274       (0 == strncmp(name, LOG_BUILTINS_SINK, len)))
275     return LOG_SERVICE_BUILTIN | LOG_SERVICE_SINK | LOG_SERVICE_SINGLETON;
276 
277   if ((len == sizeof(LOG_BUILTINS_BUFFER) - 1) &&
278       (0 == strncmp(name, LOG_BUILTINS_BUFFER, len)))
279     return LOG_SERVICE_BUILTIN | LOG_SERVICE_SINK | LOG_SERVICE_SINGLETON |
280            LOG_SERVICE_BUFFER;
281 
282   return LOG_SERVICE_UNSPECIFIED;
283 }
284 
285 /**
286   Test whether given service has *all* of the given characteristics.
287   (See log_service_chistics for a list!)
288 
289   @param  sce              service cache entry for the service in question
290   @param  required_flags   flags we're interested in (bitwise or'd)
291 
292   @retval                  true if all given flags are present, false otherwise
293 */
log_service_has_characteristics(log_service_cache_entry * sce,int required_flags)294 static inline bool log_service_has_characteristics(log_service_cache_entry *sce,
295                                                    int required_flags) {
296   return ((sce->chistics & required_flags) == required_flags);
297 }
298 
299 /**
300   Pre-defined "well-known" keys, as opposed to ad hoc ones,
301   for key/value pairs in logging.
302 */
303 typedef struct _log_item_wellknown_key {
304   const char *name;           ///< key name
305   size_t name_len;            ///< length of key's name
306   log_item_class item_class;  ///< item class (float/int/string)
307   log_item_type item_type;    ///< exact type, 1:1 relationship with name
308 } log_item_wellknown_key;
309 
310 /**
311   We support a number of predefined keys, such as "error-code" or
312   "message".  These are defined here.  We also support user-defined
313   "ad hoc" (or "generic") keys that let users of the error stack
314   add values with arbitrary keys (as long as those keys don't coincide
315   with the wellknown ones, anyway).
316 
317   The idea here is that we want the flexibility of arbitrary keys,
318   while being able to do certain optimizations for the common case.
319   This also allows us to relatively cheaply add some convenience
320   features, e.g. we know that error symbol ("ER_STARTUP") and
321   error code (1451) are related, and can supply one as the other
322   is submitted.  Likewise of course, we can use the error code to
323   fetch the associated registered error message string for that
324   error code.  Et cetera!
325 */
326 static const log_item_wellknown_key log_item_wellknown_keys[] = {
327     {STRING_WITH_LEN("--ERROR--"), LOG_UNTYPED, LOG_ITEM_END},
328     {STRING_WITH_LEN("log_type"), LOG_INTEGER, LOG_ITEM_LOG_TYPE},
329     {STRING_WITH_LEN("err_code"), LOG_INTEGER, LOG_ITEM_SQL_ERRCODE},
330     {STRING_WITH_LEN("err_symbol"), LOG_CSTRING, LOG_ITEM_SQL_ERRSYMBOL},
331     {STRING_WITH_LEN("SQL_state"), LOG_CSTRING, LOG_ITEM_SQL_STATE},
332     {STRING_WITH_LEN("OS_errno"), LOG_INTEGER, LOG_ITEM_SYS_ERRNO},
333     {STRING_WITH_LEN("OS_errmsg"), LOG_CSTRING, LOG_ITEM_SYS_STRERROR},
334     {STRING_WITH_LEN("source_file"), LOG_CSTRING, LOG_ITEM_SRC_FILE},
335     {STRING_WITH_LEN("source_line"), LOG_INTEGER, LOG_ITEM_SRC_LINE},
336     {STRING_WITH_LEN("function"), LOG_CSTRING, LOG_ITEM_SRC_FUNC},
337     {STRING_WITH_LEN("subsystem"), LOG_CSTRING, LOG_ITEM_SRV_SUBSYS},
338     {STRING_WITH_LEN("component"), LOG_CSTRING, LOG_ITEM_SRV_COMPONENT},
339     {STRING_WITH_LEN("user"), LOG_LEX_STRING, LOG_ITEM_MSC_USER},
340     {STRING_WITH_LEN("host"), LOG_LEX_STRING, LOG_ITEM_MSC_HOST},
341     {STRING_WITH_LEN("thread"), LOG_INTEGER, LOG_ITEM_SRV_THREAD},
342     {STRING_WITH_LEN("query_id"), LOG_INTEGER, LOG_ITEM_SQL_QUERY_ID},
343     {STRING_WITH_LEN("table"), LOG_CSTRING, LOG_ITEM_SQL_TABLE_NAME},
344     {STRING_WITH_LEN("prio"), LOG_INTEGER, LOG_ITEM_LOG_PRIO},
345     {STRING_WITH_LEN("label"), LOG_CSTRING, LOG_ITEM_LOG_LABEL},
346     {STRING_WITH_LEN("verbatim"), LOG_CSTRING, LOG_ITEM_LOG_VERBATIM},
347     {STRING_WITH_LEN("msg"), LOG_CSTRING, LOG_ITEM_LOG_MESSAGE},
348     {STRING_WITH_LEN("msg_id"), LOG_INTEGER, LOG_ITEM_LOG_LOOKUP},
349     {STRING_WITH_LEN("time"), LOG_CSTRING, LOG_ITEM_LOG_TIMESTAMP},
350     {STRING_WITH_LEN("ts"), LOG_INTEGER, LOG_ITEM_LOG_TS},
351     {STRING_WITH_LEN("buffered"), LOG_INTEGER, LOG_ITEM_LOG_BUFFERED},
352     {STRING_WITH_LEN("and_n_more"), LOG_INTEGER, LOG_ITEM_LOG_SUPPRESSED},
353     /*
354       We should never see the following key names in normal operations
355       (but see the user-specified key instead).  These have entries all
356       the same, covering the entirety of log_item_type, so we can use the
357       usual mechanisms for type-to-class mapping etc.
358       We could set the names to nullptr, but they're not much overhead, add
359       readability, and allow for easily creating debug info of the form,
360       "%s:%s=\"%s\"", wellknown_name, item->key, item->value
361     */
362     {STRING_WITH_LEN("misc_float"), LOG_FLOAT, LOG_ITEM_GEN_FLOAT},
363     {STRING_WITH_LEN("misc_integer"), LOG_INTEGER, LOG_ITEM_GEN_INTEGER},
364     {STRING_WITH_LEN("misc_string"), LOG_LEX_STRING, LOG_ITEM_GEN_LEX_STRING},
365     {STRING_WITH_LEN("misc_cstring"), LOG_CSTRING, LOG_ITEM_GEN_CSTRING}};
366 
367 static uint log_item_wellknown_keys_count =
368     (sizeof(log_item_wellknown_keys) / sizeof(log_item_wellknown_key));
369 
370 /*
371   string helpers
372 */
373 
374 /**
375   Compare two NUL-terminated byte strings
376 
377   Note that when comparing without length limit, the long string
378   is greater if they're equal up to the length of the shorter
379   string, but the shorter string will be considered greater if
380   its "value" up to that point is greater:
381 
382   compare 'abc','abcd':      -100  (longer wins if otherwise same)
383   compare 'abca','abcd':       -3  (higher value wins)
384   compare 'abcaaaaa','abcd':   -3  (higher value wins)
385 
386   @param  a                 the first string
387   @param  b                 the second string
388   @param  len               compare at most this many characters --
389                             0 for no limit
390   @param  case_insensitive  ignore upper/lower case in comparison
391 
392   @retval -1                a < b
393   @retval  0                a == b
394   @retval  1                a > b
395 */
log_string_compare(const char * a,const char * b,size_t len,bool case_insensitive)396 int log_string_compare(const char *a, const char *b, size_t len,
397                        bool case_insensitive) {
398   if (a == nullptr) /* purecov: begin inspected */
399     return (b == nullptr) ? 0 : -1;
400   else if (b == nullptr)
401     return 1;        /* purecov: end */
402   else if (len < 1)  // no length limit for comparison
403   {
404     return case_insensitive ? native_strcasecmp(a, b) : strcmp(a, b);
405   }
406 
407   return case_insensitive ? native_strncasecmp(a, b, len) : strncmp(a, b, len);
408 }
409 
410 /*
411   log item helpers
412 */
413 
414 /**
415   Predicate used to determine whether a type is generic
416   (generic string, generic float, generic integer) rather
417   than a well-known type.
418 
419   @param t          log item type to examine
420 
421   @retval  true     if generic type
422   @retval  false    if wellknown type
423 */
log_item_generic_type(log_item_type t)424 bool log_item_generic_type(log_item_type t) {
425   return (t & (LOG_ITEM_GEN_CSTRING | LOG_ITEM_GEN_LEX_STRING |
426                LOG_ITEM_GEN_INTEGER | LOG_ITEM_GEN_FLOAT));
427 }
428 
429 /**
430   Predicate used to determine whether a class is a string
431   class (C-string or Lex-string).
432 
433   @param c          log item class to examine
434 
435   @retval   true    if of a string class
436   @retval   false   if not of a string class
437 */
log_item_string_class(log_item_class c)438 bool log_item_string_class(log_item_class c) {
439   return ((c == LOG_CSTRING) || (c == LOG_LEX_STRING));
440 }
441 
442 /**
443   Predicate used to determine whether a class is a numeric
444   class (integer or float).
445 
446   @param c         log item class to examine
447 
448   @retval   true   if of a numeric class
449   @retval   false  if not of a numeric class
450 */
log_item_numeric_class(log_item_class c)451 bool log_item_numeric_class(log_item_class c) {
452   return ((c == LOG_INTEGER) || (c == LOG_FLOAT));
453 }
454 
455 /**
456   Get an integer value from a log-item of float or integer type.
457 
458   @param      li   log item to get the value from
459   @param[out] i    longlong to store  the value in
460 */
log_item_get_int(log_item * li,longlong * i)461 void log_item_get_int(log_item *li, longlong *i) /* purecov: begin inspected */
462 {
463   if (li->item_class == LOG_FLOAT)
464     *i = (longlong)li->data.data_float;
465   else
466     *i = (longlong)li->data.data_integer;
467 } /* purecov: end */
468 
469 /**
470   Get a float value from a log-item of float or integer type.
471 
472   @param       li      log item to get the value from
473   @param[out]  f       float to store  the value in
474 */
log_item_get_float(log_item * li,double * f)475 void log_item_get_float(log_item *li, double *f) {
476   if (li->item_class == LOG_FLOAT)
477     *f = (float)li->data.data_float;
478   else
479     *f = (float)li->data.data_integer;
480 }
481 
482 /**
483   Get a string value from a log-item of C-string or Lex string type.
484 
485   @param li            log item to get the value from
486   @param[out]  str     char-pointer   to store the pointer to the value in
487   @param[out]  len     size_t pointer to store the length of  the value in
488 */
log_item_get_string(log_item * li,char ** str,size_t * len)489 void log_item_get_string(log_item *li, char **str, size_t *len) {
490   if ((*str = const_cast<char *>(li->data.data_string.str)) == nullptr)
491     *len = 0;
492   else if (li->item_class & LOG_CSTRING)
493     *len = strlen(li->data.data_string.str);
494   else
495     *len = li->data.data_string.length;
496 }
497 
498 /**
499   See whether a string is a wellknown field name.
500 
501   @param key     potential key starts here
502   @param len     length of the string to examine
503 
504   @retval        LOG_ITEM_TYPE_RESERVED:  reserved, but not "wellknown" key
505   @retval        LOG_ITEM_TYPE_NOT_FOUND: key not found
506   @retval        >0:                      index in array of wellknowns
507 */
log_item_wellknown_by_name(const char * key,size_t len)508 int log_item_wellknown_by_name(const char *key, size_t len) {
509   uint c;
510   // optimize and safeify lookup
511   for (c = 0; (c < log_item_wellknown_keys_count); c++) {
512     if ((log_item_wellknown_keys[c].name_len == len) &&
513         (0 == native_strncasecmp(log_item_wellknown_keys[c].name, key, len))) {
514       if (log_item_generic_type(log_item_wellknown_keys[c].item_type) ||
515           (log_item_wellknown_keys[c].item_type == LOG_ITEM_END))
516         return LOG_ITEM_TYPE_RESERVED;
517       return c;
518     }
519   }
520   return LOG_ITEM_TYPE_NOT_FOUND;
521 }
522 
523 /**
524   See whether a type is wellknown.
525 
526   @param t       log item type to examine
527 
528   @retval        LOG_ITEM_TYPE_NOT_FOUND: key not found
529   @retval        >0:                      index in array of wellknowns
530 */
log_item_wellknown_by_type(log_item_type t)531 int log_item_wellknown_by_type(log_item_type t) {
532   uint c;
533   // optimize and safeify lookup
534   for (c = 0; (c < log_item_wellknown_keys_count); c++) {
535     if (log_item_wellknown_keys[c].item_type == t) return c;
536   }
537   DBUG_PRINT("warning", ("wellknown_by_type: type %d is not well-known."
538                          " Or, you know, known.",
539                          t));
540   return LOG_ITEM_TYPE_NOT_FOUND;
541 }
542 
543 /**
544   Accessor: from a record describing a wellknown key, get its name
545 
546   @param   idx  index in array of wellknowns, see log_item_wellknown_by_...()
547 
548   @retval       name (NTBS)
549 */
log_item_wellknown_get_name(uint idx)550 const char *log_item_wellknown_get_name(uint idx) {
551   return log_item_wellknown_keys[idx].name;
552 }
553 
554 /**
555   Accessor: from a record describing a wellknown key, get its type
556 
557   @param   idx  index in array of wellknowns, see log_item_wellknown_by_...()
558 
559   @retval       the log item type for the wellknown key
560 */
log_item_wellknown_get_type(uint idx)561 log_item_type log_item_wellknown_get_type(uint idx) {
562   return log_item_wellknown_keys[idx].item_type;
563 }
564 
565 /**
566   Accessor: from a record describing a wellknown key, get its class
567 
568   @param   idx  index in array of wellknowns, see log_item_wellknown_by_...()
569 
570   @retval       the log item class for the wellknown key
571 */
log_item_wellknown_get_class(uint idx)572 log_item_class log_item_wellknown_get_class(uint idx) {
573   return log_item_wellknown_keys[idx].item_class;
574 }
575 
576 /**
577   Sanity check an item.
578   Certain log sinks have very low requirements with regard to the data
579   they receive; they write keys as strings, and then data according to
580   the item's class (string, integer, or float), formatted to the sink's
581   standards (e.g. JSON, XML, ...).
582   Code that has higher requirements can use this check to see whether
583   the given item is of a known type (whether generic or wellknown),
584   whether the given type and class agree, and whether in case of a
585   well-known type, the given key is correct for that type.
586   If your code generates items that don't pass this check, you should
587   probably go meditate on it.
588 
589   @param  li  the log_item to check
590 
591   @retval LOG_ITEM_OK              no problems
592   @retval LOG_ITEM_TYPE_NOT_FOUND  unknown item type
593   @retval LOG_ITEM_CLASS_MISMATCH  item_class derived from type isn't
594                                    what's set on the item
595   @retval LOG_ITEM_KEY_MISMATCH    class not generic, so key should
596                                    match wellknown
597   @retval LOG_ITEM_STRING_NULL     class is string, pointer is nullptr
598   @retval LOG_ITEM_KEY_NULL        no key set (this is legal e.g. on aux
599                                    items of filter rules, but should not
600                                    occur in a log_line, i.e., log_sinks are
601                                    within their rights to discard such items)
602 */
log_item_inconsistent(log_item * li)603 int log_item_inconsistent(log_item *li) {
604   int w, c;
605 
606   // invalid type
607   if ((w = log_item_wellknown_by_type(li->type)) == LOG_ITEM_TYPE_NOT_FOUND)
608     return LOG_ITEM_TYPE_NOT_FOUND;
609 
610   // fetch expected storage class for this type
611   if ((c = log_item_wellknown_keys[w].item_class) == LOG_CSTRING)
612     c = LOG_LEX_STRING;
613 
614   // class and type don't match
615   if (c != li->item_class) return LOG_ITEM_CLASS_MISMATCH;
616 
617   // no key set
618   if (li->key == nullptr) return LOG_ITEM_KEY_NULL;
619 
620   // it's not a generic, and key and type don't match
621   if (!log_item_generic_type(li->type) &&
622       (0 != strcmp(li->key, log_item_wellknown_keys[w].name)))
623     return LOG_ITEM_KEY_MISMATCH;
624 
625   // strings should have non-nullptr
626   if ((c == LOG_LEX_STRING) && (li->data.data_string.str == nullptr))
627     return LOG_ITEM_STRING_NULL;
628 
629   return LOG_ITEM_OK;
630 }
631 
632 /**
633   Release any of key and value on a log-item that were dynamically allocated.
634 
635   @param  li  log-item to release the payload of
636 */
log_item_free(log_item * li)637 void log_item_free(log_item *li) {
638   if (li->alloc & LOG_ITEM_FREE_KEY) my_free(const_cast<char *>(li->key));
639 
640   if (li->alloc & LOG_ITEM_FREE_VALUE) {
641     if (li->item_class == LOG_LEX_STRING)
642       my_free(const_cast<char *>(li->data.data_string.str));
643     else
644       DBUG_ASSERT(false);
645   }
646 
647   li->alloc = LOG_ITEM_FREE_NONE;
648 }
649 
650 /**
651   Dynamically allocate and initialize a log_line.
652 
653   @retval nullptr  could not set up buffer (too small?)
654   @retval other    address of the newly initialized log_line
655 */
log_line_init()656 log_line *log_line_init() {
657   log_line *ll;
658   if ((ll = (log_line *)my_malloc(key_memory_log_error_stack, sizeof(log_line),
659                                   MYF(0))) != nullptr)
660     memset(ll, 0, sizeof(log_line));
661   return ll;
662 }
663 
664 /**
665   Release a log_line allocated with line_init()
666 
667   @param  ll       a log_line previously allocated with line_init()
668 */
log_line_exit(log_line * ll)669 void log_line_exit(log_line *ll) {
670   if (ll != nullptr) my_free(ll);
671 }
672 
673 /**
674   Predicate indicating whether a log line is "willing" to accept any more
675   key/value pairs.
676 
677   @param   ll     the log-line to examine
678 
679   @retval  false  if not full / if able to accept another log_item
680   @retval  true   if full
681 */
log_line_full(log_line * ll)682 bool log_line_full(log_line *ll) {
683   return ((ll == nullptr) || (ll->count >= LOG_ITEM_MAX));
684 }
685 
686 /**
687   Duplicate a log-event. This is a deep copy where the items (key/value pairs)
688   have their own allocated memory separate from that in the source item.
689 
690   @param   dst    log_line that will hold the copy
691   @param   src    log_line we copy from
692 
693   @retval  false  on success
694   @retval  true   if out of memory
695 */
log_line_duplicate(log_line * dst,log_line * src)696 bool log_line_duplicate(log_line *dst, log_line *src) {
697   int c;
698 
699   DBUG_ASSERT((dst != nullptr) && (src != nullptr));
700 
701   *dst = *src;
702 
703   for (c = 0; c < src->count; c++) {
704     dst->item[c].alloc = LOG_ITEM_FREE_NONE;
705 
706     if ((dst->item[c].key =
707              my_strndup(key_memory_log_error_loaded_services, src->item[c].key,
708                         strlen(src->item[c].key), MYF(0))) != nullptr) {
709       // We just allocated a key, remember to free it later:
710       dst->item[c].alloc = LOG_ITEM_FREE_KEY;
711 
712       // If the value is a string, duplicate it, and remember to free it later!
713       if (log_item_string_class(src->item[c].item_class) &&
714           (src->item[c].data.data_string.str != nullptr)) {
715         if ((dst->item[c].data.data_string.str = my_strndup(
716                  key_memory_log_error_loaded_services,
717                  src->item[c].data.data_string.str,
718                  src->item[c].data.data_string.length, MYF(0))) != nullptr)
719           dst->item[c].alloc |= LOG_ITEM_FREE_VALUE;
720         else
721           goto fail; /* purecov: inspected */
722       }
723     } else
724       goto fail; /* purecov: inspected */
725   }
726 
727   return false;
728 
729 fail:                           /* purecov: begin inspected */
730   dst->count = c + 1;           // consider only the items we actually processed
731   log_line_item_free_all(dst);  // free those items
732   return true;                  // flag failure
733   /* purecov: end */
734 }
735 
736 /**
737   How many items are currently set on the given log_line?
738 
739   @param   ll     the log-line to examine
740 
741   @retval         the number of items set
742 */
log_line_item_count(log_line * ll)743 int log_line_item_count(log_line *ll) { return ll->count; }
744 
745 /**
746   Test whether a given type is presumed present on the log line.
747 
748   @param  ll  the log_line to examine
749   @param  m   the log_type to test for
750 
751   @retval  0  not present
752   @retval !=0 present
753 */
log_line_item_types_seen(log_line * ll,log_item_type_mask m)754 log_item_type_mask log_line_item_types_seen(log_line *ll,
755                                             log_item_type_mask m) {
756   return (ll != nullptr) ? (ll->seen & m) : 0;
757 }
758 
759 /**
760   Release log line item (key/value pair) with the index elem in log line ll.
761   This frees whichever of key and value were dynamically allocated.
762   This leaves a "gap" in the bag that may immediately be overwritten
763   with an updated element.  If the intention is to remove the item without
764   replacing it, use log_line_item_remove() instead!
765 
766   @param         ll    log_line
767   @param         elem  index of the key/value pair to release
768 */
log_line_item_free(log_line * ll,size_t elem)769 void log_line_item_free(log_line *ll, size_t elem) {
770   DBUG_ASSERT(ll->count > 0);
771   log_item_free(&(ll->item[elem]));
772 }
773 
774 /**
775   Release all log line items (key/value pairs) in log line ll.
776   This frees whichever keys and values were dynamically allocated.
777 
778   @param         ll    log_line
779 */
log_line_item_free_all(log_line * ll)780 void log_line_item_free_all(log_line *ll) {
781   while (ll->count > 0) log_item_free(&(ll->item[--ll->count]));
782   ll->seen = LOG_ITEM_END;
783 }
784 
785 /**
786   Release log line item (key/value pair) with the index elem in log line ll.
787   This frees whichever of key and value were dynamically allocated.
788   This moves any trailing items to fill the "gap" and decreases the counter
789   of elements in the log line.  If the intention is to leave a "gap" in the
790   bag that may immediately be overwritten with an updated element, use
791   log_line_item_free() instead!
792 
793   @param         ll    log_line
794   @param         elem  index of the key/value pair to release
795 */
log_line_item_remove(log_line * ll,int elem)796 void log_line_item_remove(log_line *ll, int elem) {
797   DBUG_ASSERT(ll->count > 0);
798 
799   log_line_item_free(ll, elem);
800 
801   // Fill the gap if needed (if there are more elements and we're not the tail)
802   if ((ll->count > 1) && (elem < (ll->count - 1)))
803     ll->item[elem] = ll->item[ll->count - 1];
804 
805   ll->count--;
806 }
807 
808 /**
809   Find the (index of the) last key/value pair of the given name
810   in the log line.
811 
812   @param         ll   log line
813   @param         key  the key to look for
814 
815   @retval        -1:  none found
816   @retval        -2:  invalid search-key given
817   @retval        -3:  no log_line given
818   @retval        >=0: index of the key/value pair in the log line
819 */
log_line_index_by_name(log_line * ll,const char * key)820 int log_line_index_by_name(log_line *ll, const char *key) {
821   uint32 count = ll->count;
822 
823   if (ll == nullptr) /* purecov: begin inspected */
824     return -3;
825   else if ((key == nullptr) || (key[0] == '\0'))
826     return -2; /* purecov: end */
827 
828   /*
829     As later items overwrite earlier ones, return the rightmost match!
830   */
831   while (count > 0) {
832     if (0 == strcmp(ll->item[--count].key, key)) return count;
833   }
834 
835   return -1;
836 }
837 
838 /**
839   Find the last item matching the given key in the log line.
840 
841   @param         ll   log line
842   @param         key  the key to look for
843 
844   @retval        nullptr    item not found
845   @retval        otherwise  pointer to the item (not a copy thereof!)
846 */
log_line_item_by_name(log_line * ll,const char * key)847 log_item *log_line_item_by_name(log_line *ll, const char *key) {
848   int i = log_line_index_by_name(ll, key);
849   return (i < 0) ? nullptr : &ll->item[i];
850 }
851 
852 /**
853   Find the (index of the) last key/value pair of the given type
854   in the log line.
855 
856   @param         ll   log line
857   @param         t    the log item type to look for
858 
859   @retval        <0:  none found
860   @retval        >=0: index of the key/value pair in the log line
861 */
log_line_index_by_type(log_line * ll,log_item_type t)862 int log_line_index_by_type(log_line *ll, log_item_type t) {
863   uint32 count = ll->count;
864 
865   /*
866     As later items overwrite earlier ones, return the rightmost match!
867   */
868   while (count > 0) {
869     if (ll->item[--count].type == t) return count;
870   }
871 
872   return -1;
873 }
874 
875 /**
876   Find the (index of the) last key/value pair of the given type
877   in the log line. This variant accepts a reference item and looks
878   for an item that is of the same type (for wellknown types), or
879   one that is of a generic type, and with the same key name (for
880   generic types).  For example, a reference item containing a
881   generic string with key "foo" will a generic string, integer, or
882   float with the key "foo".
883 
884   @param         ll   log line
885   @param         ref  a reference item of the log item type to look for
886 
887   @retval        <0:  none found
888   @retval        >=0: index of the key/value pair in the log line
889 */
log_line_index_by_item(log_line * ll,log_item * ref)890 int log_line_index_by_item(log_line *ll, log_item *ref) {
891   uint32 count = ll->count;
892 
893   if (log_item_generic_type(ref->type)) {
894     while (count > 0) {
895       count--;
896 
897       if (log_item_generic_type(ll->item[count].type) &&
898           (native_strcasecmp(ref->key, ll->item[count].key) == 0))
899         return count;
900     }
901   } else {
902     while (count > 0) {
903       if (ll->item[--count].type == ref->type) return count;
904     }
905   }
906 
907   return -1;
908 }
909 
910 /**
911   Initializes a log entry for use. This simply puts it in a defined
912   state; if you wish to reset an existing item, see log_item_free().
913 
914   @param  li  the log-item to initialize
915 */
log_item_init(log_item * li)916 void log_item_init(log_item *li) { memset(li, 0, sizeof(log_item)); }
917 
918 /**
919   Initializes an entry in a log line for use. This simply puts it in
920   a defined state; if you wish to reset an existing item, see
921   log_item_free().
922   This resets the element beyond the last. The element count is not
923   adjusted; this is for the caller to do once it sets up a valid
924   element to suit its needs in the cleared slot. Finally, it is up
925   to the caller to make sure that an element can be allocated.
926 
927   @param  ll  the log-line to initialize a log_item in
928 
929   @retval     the address of the cleared log_item
930 */
log_line_item_init(log_line * ll)931 log_item *log_line_item_init(log_line *ll) {
932   log_item_init(&ll->item[ll->count]);
933   return &ll->item[ll->count];
934 }
935 
936 /**
937   Create new log item with key name "key", and allocation flags of
938   "alloc" (see enum_log_item_free).
939   Will return a pointer to the item's log_item_data struct for
940   convenience.
941   This is mostly interesting for filters and other services that create
942   items that are not part of a log_line; sources etc. that intend to
943   create an item for a log_line (the more common case) should usually
944   use the below line_item_set_with_key() which creates an item (like
945   this function does), but also correctly inserts it into a log_line.
946 
947   @param  li     the log_item to work on
948   @param  t      the item-type
949   @param  key    the key to set on the item.
950                  ignored for non-generic types (may pass nullptr for those)
951                  see alloc
952   @param  alloc  LOG_ITEM_FREE_KEY  if key was allocated by caller
953                  LOG_ITEM_FREE_NONE if key was not allocated
954                  Allocated keys will automatically free()d when the
955                  log_item is.
956                  The log_item's alloc flags will be set to the
957                  submitted value; specifically, any pre-existing
958                  value will be clobbered.  It is therefore WRONG
959                  a) to use this on a log_item that already has a key;
960                     it should only be used on freshly init'd log_items;
961                  b) to use this on a log_item that already has a
962                     value (specifically, an allocated one); the correct
963                     order is to init a log_item, then set up type and
964                     key, and finally to set the value. If said value is
965                     an allocated string, the log_item's alloc should be
966                     bitwise or'd with LOG_ITEM_FREE_VALUE.
967 
968   @retval        a pointer to the log_item's log_data, for easy chaining:
969                  log_item_set_with_key(...)->data_integer= 1;
970 */
log_item_set_with_key(log_item * li,log_item_type t,const char * key,uint32 alloc)971 log_item_data *log_item_set_with_key(log_item *li, log_item_type t,
972                                      const char *key, uint32 alloc) {
973   int c = log_item_wellknown_by_type(t);
974 
975   li->alloc = alloc;
976   if (log_item_generic_type(t)) {
977     li->key = key;
978   } else {
979     li->key = log_item_wellknown_keys[c].name;
980     DBUG_ASSERT((alloc & LOG_ITEM_FREE_KEY) == 0);
981   }
982 
983   // If we accept a C-string as input, it'll become a Lex string internally
984   if ((li->item_class = log_item_wellknown_keys[c].item_class) == LOG_CSTRING)
985     li->item_class = LOG_LEX_STRING;
986 
987   li->type = t;
988 
989   DBUG_ASSERT(((alloc & LOG_ITEM_FREE_VALUE) == 0) ||
990               (li->item_class == LOG_CSTRING) ||
991               (li->item_class == LOG_LEX_STRING));
992 
993   return &li->data;
994 }
995 
996 /**
997   Create new log item in log line "ll", with key name "key", and
998   allocation flags of "alloc" (see enum_log_item_free).
999   On success, the number of registered items on the log line is increased,
1000   the item's type is added to the log_line's "seen" property,
1001   and a pointer to the item's log_item_data struct is returned for
1002   convenience.
1003 
1004   @param  ll        the log_line to work on
1005   @param  t         the item-type
1006   @param  key       the key to set on the item.
1007                     ignored for non-generic types (may pass nullptr for those)
1008                     see alloc
1009   @param  alloc     LOG_ITEM_FREE_KEY  if key was allocated by caller
1010                     LOG_ITEM_FREE_NONE if key was not allocated
1011                     Allocated keys will automatically free()d when the
1012                     log_item is.
1013                     The log_item's alloc flags will be set to the
1014                     submitted value; specifically, any pre-existing
1015                     value will be clobbered.  It is therefore WRONG
1016                     a) to use this on a log_item that already has a key;
1017                        it should only be used on freshly init'd log_items;
1018                     b) to use this on a log_item that already has a
1019                        value (specifically, an allocated one); the correct
1020                        order is to init a log_item, then set up type and
1021                        key, and finally to set the value. If said value is
1022                        an allocated string, the log_item's alloc should be
1023                        bitwise or'd with LOG_ITEM_FREE_VALUE.
1024 
1025   @retval !nullptr  a pointer to the log_item's log_data, for easy chaining:
1026                     log_line_item_set_with_key(...)->data_integer= 1;
1027   @retval  nullptr  could not create a log_item in given log_line
1028 */
log_line_item_set_with_key(log_line * ll,log_item_type t,const char * key,uint32 alloc)1029 log_item_data *log_line_item_set_with_key(log_line *ll, log_item_type t,
1030                                           const char *key, uint32 alloc) {
1031   log_item *li;
1032 
1033   if (log_line_full(ll)) return nullptr;
1034 
1035   li = &(ll->item[ll->count]);
1036 
1037   log_item_set_with_key(li, t, key, alloc);
1038   ll->seen |= t;
1039   ll->count++;
1040 
1041   return &li->data;
1042 }
1043 
1044 /**
1045   As log_item_set_with_key(), except that the key is automatically
1046   derived from the wellknown log_item_type t.
1047 
1048   Create new log item with type "t".
1049   Will return a pointer to the item's log_item_data struct for
1050   convenience.
1051   This is mostly interesting for filters and other services that create
1052   items that are not part of a log_line; sources etc. that intend to
1053   create an item for a log_line (the more common case) should usually
1054   use the below line_item_set_with_key() which creates an item (like
1055   this function does), but also correctly inserts it into a log_line.
1056 
1057   The allocation of this item will be LOG_ITEM_FREE_NONE;
1058   specifically, any pre-existing value will be clobbered.
1059   It is therefore WRONG
1060   a) to use this on a log_item that already has a key;
1061      it should only be used on freshly init'd log_items;
1062   b) to use this on a log_item that already has a
1063      value (specifically, an allocated one); the correct
1064      order is to init a log_item, then set up type and
1065      key, and finally to set the value. If said value is
1066      an allocated string, the log_item's alloc should be
1067      bitwise or'd with LOG_ITEM_FREE_VALUE.
1068 
1069   @param  li     the log_item to work on
1070   @param  t      the item-type
1071 
1072   @retval        a pointer to the log_item's log_data, for easy chaining:
1073                  log_item_set_with_key(...)->data_integer= 1;
1074 */
log_item_set(log_item * li,log_item_type t)1075 log_item_data *log_item_set(log_item *li, log_item_type t) {
1076   return log_item_set_with_key(li, t, nullptr, LOG_ITEM_FREE_NONE);
1077 }
1078 
1079 /**
1080   Create a new log item of well-known type "t" in log line "ll".
1081   On success, the number of registered items on the log line is increased,
1082   the item's type is added to the log_line's "seen" property,
1083   and a pointer to the item's log_item_data struct is returned for
1084   convenience.
1085 
1086   The allocation of this item will be LOG_ITEM_FREE_NONE;
1087   specifically, any pre-existing value will be clobbered.
1088   It is therefore WRONG
1089   a) to use this on a log_item that already has a key;
1090      it should only be used on freshly init'd log_items;
1091   b) to use this on a log_item that already has a
1092      value (specifically, an allocated one); the correct
1093      order is to init a log_item, then set up type and
1094      key, and finally to set the value. If said value is
1095      an allocated string, the log_item's alloc should be
1096      bitwise or'd with LOG_ITEM_FREE_VALUE.
1097 
1098   @param  ll        the log_line to work on
1099   @param  t         the item-type
1100 
1101   @retval !nullptr  a pointer to the log_item's log_data, for easy chaining:
1102                     log_line_item_set(...)->data_integer= 1;
1103   @retval  nullptr  could not create a log_item in given log_line
1104 */
log_line_item_set(log_line * ll,log_item_type t)1105 log_item_data *log_line_item_set(log_line *ll, log_item_type t) {
1106   return log_line_item_set_with_key(ll, t, nullptr, LOG_ITEM_FREE_NONE);
1107 }
1108 
1109 /**
1110   Set an integer value on a log_item.
1111   Fails gracefully if no log_item_data is supplied, so it can safely
1112   wrap log_line_item_set[_with_key]().
1113 
1114   @param  lid    log_item_data struct to set the value on
1115   @param  i      integer to set
1116 
1117   @retval true   lid was nullptr (possibly: OOM, could not set up log_item)
1118   @retval false  all's well
1119 */
log_item_set_int(log_item_data * lid,longlong i)1120 bool log_item_set_int(log_item_data *lid, longlong i) {
1121   if (lid != nullptr) {
1122     lid->data_integer = i;
1123     return false;
1124   }
1125   return true;
1126 }
1127 
1128 /**
1129   Set a floating point value on a log_item.
1130   Fails gracefully if no log_item_data is supplied, so it can safely
1131   wrap log_line_item_set[_with_key]().
1132 
1133   @param  lid    log_item_data struct to set the value on
1134   @param  f      float to set
1135 
1136   @retval true   lid was nullptr (possibly: OOM, could not set up log_item)
1137   @retval false  all's well
1138 */
log_item_set_float(log_item_data * lid,double f)1139 bool log_item_set_float(log_item_data *lid, double f) {
1140   if (lid != nullptr) {
1141     lid->data_float = f;
1142     return false;
1143   }
1144   return true;
1145 }
1146 
1147 /**
1148   Set a string value on a log_item.
1149   Fails gracefully if no log_item_data is supplied, so it can safely
1150   wrap log_line_item_set[_with_key]().
1151 
1152   @param  lid    log_item_data struct to set the value on
1153   @param  s      pointer to string
1154   @param  s_len  length of string
1155 
1156   @retval true   lid was nullptr (possibly: OOM, could not set up log_item)
1157   @retval false  all's well
1158 */
log_item_set_lexstring(log_item_data * lid,const char * s,size_t s_len)1159 bool log_item_set_lexstring(log_item_data *lid, const char *s, size_t s_len) {
1160   if (lid != nullptr) {
1161     lid->data_string.str = (s == nullptr) ? "" : s;
1162     lid->data_string.length = s_len;
1163     return false;
1164   }
1165   return true;
1166 }
1167 
1168 /**
1169   Set a string value on a log_item.
1170   Fails gracefully if no log_item_data is supplied, so it can safely
1171   wrap log_line_item_set[_with_key]().
1172 
1173   @param  lid    log_item_data struct to set the value on
1174   @param  s      pointer to NTBS
1175 
1176   @retval true   lid was nullptr (possibly: OOM, could not set up log_item)
1177   @retval false  all's well
1178 */
log_item_set_cstring(log_item_data * lid,const char * s)1179 bool log_item_set_cstring(log_item_data *lid, const char *s) {
1180   if (lid != nullptr) {
1181     lid->data_string.str = (s == nullptr) ? "" : s;
1182     lid->data_string.length = strlen(lid->data_string.str);
1183     return false;
1184   }
1185   return true;
1186 }
1187 
1188 /**
1189   Convenience function: Derive a log label ("error", "warning",
1190   "information") from a severity.
1191 
1192   @param   prio       the severity/prio in question
1193 
1194   @return             a label corresponding to that priority.
1195   @retval  "System"   for prio of SYSTEM_LEVEL
1196   @retval  "Error"    for prio of ERROR_LEVEL
1197   @retval  "Warning"  for prio of WARNING_LEVEL
1198   @retval  "Note"     for prio of INFORMATION_LEVEL
1199 */
log_label_from_prio(int prio)1200 const char *log_label_from_prio(int prio) {
1201   switch (prio) {
1202     case SYSTEM_LEVEL:
1203       return "System";
1204     case ERROR_LEVEL:
1205       return "Error";
1206     case WARNING_LEVEL:
1207       return "Warning";
1208     case INFORMATION_LEVEL:
1209       return "Note";
1210     default:
1211       DBUG_ASSERT(false);
1212       return "";
1213   }
1214 }
1215 
1216 /**
1217   services: log sinks: basic logging ("classic error-log")
1218   Will write timestamp, label, thread-ID, and message to stderr/file.
1219   If you should not be able to specify a label, one will be generated
1220   for you from the line's priority field.
1221 
1222   @param           instance             instance handle
1223   @param           ll                   the log line to write
1224 
1225   @retval          int                  number of added fields, if any
1226 */
log_sink_trad(void * instance MY_ATTRIBUTE ((unused)),log_line * ll)1227 static int log_sink_trad(void *instance MY_ATTRIBUTE((unused)), log_line *ll) {
1228   const char *label = "", *msg = "";
1229   int c, out_fields = 0;
1230   size_t msg_len = 0, iso_len = 0, label_len = 0, subsys_len = 0;
1231   enum loglevel prio = ERROR_LEVEL;
1232   unsigned int errcode = 0;
1233   log_item_type item_type = LOG_ITEM_END;
1234   log_item_type_mask out_types = 0;
1235   const char *iso_timestamp = "", *subsys = "";
1236   my_thread_id thread_id = 0;
1237   ulonglong microtime = 0;
1238 
1239   if (ll->count > 0) {
1240     for (c = 0; c < ll->count; c++) {
1241       item_type = ll->item[c].type;
1242 
1243       if (log_item_inconsistent(&ll->item[c])) continue;
1244 
1245       out_fields++;
1246 
1247       switch (item_type) {
1248         case LOG_ITEM_LOG_BUFFERED:
1249           microtime = (ulonglong)ll->item[c].data.data_integer;
1250           break;
1251         case LOG_ITEM_SQL_ERRCODE:
1252           errcode = (unsigned int)ll->item[c].data.data_integer;
1253           break;
1254         case LOG_ITEM_LOG_PRIO:
1255           prio = (enum loglevel)ll->item[c].data.data_integer;
1256           break;
1257         case LOG_ITEM_LOG_MESSAGE:
1258           msg = ll->item[c].data.data_string.str;
1259           msg_len = ll->item[c].data.data_string.length;
1260           break;
1261         case LOG_ITEM_LOG_LABEL:
1262           label = ll->item[c].data.data_string.str;
1263           label_len = ll->item[c].data.data_string.length;
1264           break;
1265         case LOG_ITEM_SRV_SUBSYS:
1266           subsys = ll->item[c].data.data_string.str;
1267           if ((subsys_len = ll->item[c].data.data_string.length) > 12)
1268             subsys_len = 12;
1269           break;
1270         case LOG_ITEM_LOG_TIMESTAMP:
1271           iso_timestamp = ll->item[c].data.data_string.str;
1272           iso_len = ll->item[c].data.data_string.length;
1273           break;
1274         case LOG_ITEM_SRV_THREAD:
1275           thread_id = (my_thread_id)ll->item[c].data.data_integer;
1276           break;
1277         default:
1278           out_fields--;
1279       }
1280       out_types |= item_type;
1281     }
1282 
1283     /*
1284       Prevent duplicates in incremental flush of buffered log events.
1285       If start-up is very slow, we'll let error-log event buffering
1286       "time out": we'll print the events so far to the traditional
1287       error log (and only to that log, as external log-writers cannot
1288       be loaded until later, when InnoDB, upon which the component
1289       framework depends, has been initialized). Then, we'll go back
1290       to buffering. Eventually, we'll either time-out and flush again,
1291       or start-up completes, and we can do a proper flush of the
1292       buffered error-events.
1293       At that point, any loaded log-writers will be called for the
1294       first time, whereas this here traditional writer may have been
1295       called upon multiple times to give incremental updates.
1296       Therefore, we must prevent duplicates in this, and only in this
1297       writer. Ready? (Who lives in a time-out under the C? Buffered
1298       events, in trad sink!)
1299     */
1300     if (out_types & LOG_ITEM_LOG_BUFFERED) {
1301       // abort if we've already printed this (to trad log only!)
1302       if (microtime <= log_sink_trad_last) return 0;
1303       // remember micro-time of last buffered event we've printed
1304       log_sink_trad_last = microtime;
1305     }
1306 
1307     if (!(out_types & LOG_ITEM_LOG_MESSAGE)) {
1308       msg =
1309           "No error message, or error message of non-string type. "
1310           "This is almost certainly a bug!";
1311       msg_len = strlen(msg);
1312 
1313       prio = ERROR_LEVEL;                  // force severity
1314       out_types &= ~(LOG_ITEM_LOG_LABEL);  // regenerate label
1315       out_types |= LOG_ITEM_LOG_MESSAGE;   // we added a message
1316     }
1317 
1318     {
1319       char buff_line[LOG_BUFF_MAX];
1320       size_t len;
1321 
1322       if (!(out_types & LOG_ITEM_LOG_LABEL)) {
1323         label = (prio == ERROR_LEVEL) ? "ERROR" : log_label_from_prio(prio);
1324         label_len = strlen(label);
1325       }
1326 
1327       if (!(out_types & LOG_ITEM_LOG_TIMESTAMP)) {
1328         char buff_local_time[iso8601_size];
1329 
1330         make_iso8601_timestamp(buff_local_time, my_micro_time(),
1331                                opt_log_timestamps);
1332         iso_timestamp = buff_local_time;
1333         iso_len = strlen(buff_local_time);
1334       }
1335 
1336       /*
1337         WL#11009 adds "error identifier" as a field in square brackets
1338         that directly precedes the error message. As a result, new
1339         tools can check for the presence of this field by testing
1340         whether the first character of the presumed message field is '['.
1341         Older tools will just consider this identifier part of the
1342         message; this should therefore not affect log aggregation.
1343         Tools reacting to the contents of the message may wish to
1344         use the new field instead as it's simpler to parse.
1345         The rules are like so:
1346 
1347           '[' [ <namespace> ':' ] <identifier> ']'
1348 
1349         That is, an error identifier may be namespaced by a
1350         subsystem/component name and a ':'; the identifier
1351         itself should be considered opaque; in particular, it
1352         may be non-numerical: [ <alpha> | <digit> | '_' | '.' | '-' ]
1353       */
1354       len = snprintf(buff_line, sizeof(buff_line),
1355                      "%.*s %u [%.*s] [MY-%06u] [%.*s] %.*s", (int)iso_len,
1356                      iso_timestamp, thread_id, (int)label_len, label, errcode,
1357                      (int)subsys_len, subsys, (int)msg_len, msg);
1358 
1359       log_write_errstream(buff_line, len);
1360 
1361       return out_fields;  // returning number of processed items
1362     }
1363   }
1364   return 0;
1365 }
1366 
1367 /**
1368   services: log sinks: buffered logging
1369 
1370   During start-up, we buffer log-info until a) we have basic info for
1371   the built-in logger (what file to log to, verbosity, and so on), and
1372   b) advanced info (any logging components to load, any configuration
1373   for them, etc.).
1374 
1375   As a failsafe, if start-up takes very, very long, and a time-out is
1376   reached before reaching b) and we actually have something worth
1377   reporting (e.g. errors, as opposed to info), we try to keep the user
1378   informed by using the basic logger configured in a), while going on
1379   buffering all info and flushing it to any advanced loggers when b)
1380   is reached.
1381 
1382   1) This function checks and, if needed, updates the time-out, and calls
1383      the flush functions as needed. It is internal to the logger and should
1384      not be called from elsewhere.
1385 
1386   2) Function will save log-event (if given) for later filtering and output.
1387 
1388   3) Function acquires/releases THR_LOCK_log_buffered if initialized.
1389 
1390   @param           instance             instance handle
1391                                         Not currently used in this writer;
1392                                         if this changes later, keep in mind
1393                                         that nullptr will be passed if this
1394                                         is called before the structured
1395                                         logger's locks are initialized, so
1396                                         that must remain a valid argument!
1397   @param           ll                   The log line to write,
1398                                         or nullptr to not add a new logline,
1399                                         but to just check whether the time-out
1400                                         has been reached and if so, flush
1401                                         as needed.
1402 
1403   @retval          -1                   can not add event to buffer (OOM?)
1404   @retval          >0                   number of added fields
1405 */
log_sink_buffer(void * instance MY_ATTRIBUTE ((unused)),log_line * ll)1406 static int log_sink_buffer(void *instance MY_ATTRIBUTE((unused)),
1407                            log_line *ll) {
1408   log_line_buffer *llb = nullptr;  ///< log-line buffer
1409   ulonglong now = 0;
1410   int count = 0;
1411 
1412   /*
1413     If we were actually given an event, add it to the buffer.
1414   */
1415   if (ll != nullptr) {
1416     if ((llb = (log_line_buffer *)my_malloc(key_memory_log_error_stack,
1417                                             sizeof(log_line_buffer), MYF(0))) ==
1418         nullptr)
1419       return -1; /* purecov: inspected */
1420 
1421     llb->next = nullptr;
1422 
1423     /*
1424       Don't let the submitter free the keys/values; we'll do it later when
1425       the buffer is flushed and then de-allocated!
1426       (No lock needed for copy as the target-event is still private to this
1427       function, and the source-event is alloc'd in the caller so will be
1428       there at least until we return.)
1429     */
1430     log_line_duplicate(&llb->ll, ll);
1431 
1432     /*
1433       Remember it when an ERROR or SYSTEM prio event was buffered.
1434       If buffered logging times out and the buffer contains such an event,
1435       we force a premature flush so the user will know what's going on.
1436     */
1437     {
1438       int index_prio = log_line_index_by_type(&llb->ll, LOG_ITEM_LOG_PRIO);
1439 
1440       if ((index_prio >= 0) &&
1441           (llb->ll.item[index_prio].data.data_integer <= ERROR_LEVEL))
1442         log_buffering_flushworthy = true;
1443     }
1444   }
1445 
1446   /*
1447     Insert the new last event into the buffer
1448     (a singly linked list of events).
1449   */
1450   if (log_builtins_inited) mysql_mutex_lock(&THR_LOCK_log_buffered);
1451 
1452   now = my_micro_time();
1453 
1454   if (ll != nullptr) {
1455     /*
1456       Prevent two events from receiving the exact same timestamp on
1457       systems with low resolution clocks.
1458     */
1459     if (now > log_sink_buffer_last)
1460       log_sink_buffer_last = now;
1461     else
1462       log_sink_buffer_last++;
1463 
1464     /*
1465       Save the current time so we can regenerate the textual timestamp
1466       later when we have the command-line options telling us what format
1467       it should be in (e.g. UTC or system time).
1468     */
1469     if (!log_line_full(&llb->ll)) {
1470       log_line_item_set(&llb->ll, LOG_ITEM_LOG_BUFFERED)->data_integer =
1471           log_sink_buffer_last;
1472     }
1473 
1474     *log_line_buffer_tail = llb;
1475     log_line_buffer_tail = &(llb->next);
1476 
1477     /*
1478       Save the element-count now as the time-out flush below may release
1479       the underlying log line buffer, llb, making that info inaccessible.
1480     */
1481     count = llb->ll.count;
1482   }
1483 
1484   /*
1485     Handle buffering time-out!
1486   */
1487 
1488   // Buffering very first event; set up initial time-out ...
1489   if (log_buffering_timeout == 0)
1490     log_buffering_timeout =
1491         now + (LOG_BUFFERING_TIMEOUT_AFTER * LOG_BUFFERING_TIME_SCALE);
1492 
1493   if (log_error_stage_get() == LOG_ERROR_STAGE_BUFFERING_UNIPLEX) {
1494     /*
1495       When not multiplexing several log-writers into the same stream,
1496       we need not delay.
1497     */
1498     log_sink_buffer_flush(LOG_BUFFER_REPORT_AND_KEEP);
1499   }
1500 
1501   // Need to flush? Check on time-out, or when explicitly asked to.
1502   else if ((now > log_buffering_timeout) || (ll == nullptr)) {
1503     // We timed out. Flush, and set up new, possibly shorter subsequent timeout.
1504 
1505     /*
1506       Timing out is somewhat awkward. Ideally, it shouldn't happen at all;
1507       but as long as it does, it is extremely unlikely to happen during early
1508       set-up -- instead, it might happen during engine set-up ("cannot lock
1509       DB file, another server instance already has it", "applying large
1510       binlog", etc.).
1511 
1512       The good news is that this means we've already parsed the command line
1513       options; we have the timestamp format, the error log file, the verbosity,
1514       and the suppression list (if any).
1515 
1516       The bad news is, if the engine we're waiting for is InnoDB, which the
1517       component framework persists its component list in, the log-writers
1518       selected by the DBA won't have been loaded yet. This of course means
1519       that the time-out flushes will only be available in the built-in,
1520       "traditional" format, but at least this way, the user gets updates
1521       during long waits. Additionally if the server exits during start-up
1522       (i.e. before full logging is available), we'll have log info of what
1523       happened (albeit not in the preferred format).
1524 
1525       If start-up completes and external log-services become available,
1526       we will flush all buffered messages to any external log-writers
1527       requested by the user (using their preferred log-filtering set-up
1528       as well).
1529     */
1530 
1531     // If anything of sufficient priority is in the buffer ...
1532     if (log_buffering_flushworthy) {
1533       /*
1534         ... log it now to the traditional log, but keep the buffered
1535         events around in case we need to write them to loadable log-sinks
1536         later.
1537       */
1538       log_sink_buffer_flush(LOG_BUFFER_REPORT_AND_KEEP);
1539       // Remember that all high-prio events so far have now been shown.
1540       log_buffering_flushworthy = false;
1541     }
1542 
1543     // Whether we've needed to flush or not, start a new window:
1544     log_buffering_timeout =
1545         now + (LOG_BUFFERING_TIMEOUT_EVERY * LOG_BUFFERING_TIME_SCALE);
1546   }
1547 
1548   if (log_builtins_inited) mysql_mutex_unlock(&THR_LOCK_log_buffered);
1549 
1550   return count;
1551 }
1552 
1553 /**
1554   Convenience function. Same as log_sink_buffer(), except we only
1555   check whether the time-out for buffered logging has been reached
1556   during start-up, and act accordingly; no new logging information
1557   is added (i.e., we only provide functionality 1 described in the
1558   preamble for log_sink_buffer() above).
1559 */
log_sink_buffer_check_timeout(void)1560 void log_sink_buffer_check_timeout(void) { log_sink_buffer(nullptr, nullptr); }
1561 
1562 /**
1563   Process all buffered log-events.
1564 
1565   LOG_BUFFER_DISCARD_ONLY simply releases all buffered log-events
1566   (used when called from discard_error_log_messages()).
1567 
1568   LOG_BUFFER_PROCESS_AND_DISCARD sends the events through the
1569   configured error log stack first before discarding them.
1570   (used when called from flush_error_log_messages()).
1571   Must remain safe to call repeatedly (with subsequent calls only
1572   outputting any events added after the previous flush).
1573 
1574   LOG_BUFFER_REPORT_AND_KEEP is used to show incremental status
1575   updates when the start-up takes unusually long. When that happens,
1576   we "report" the intermediate state using the built-in sink
1577   (as that's the only one we available to us at the time), and
1578   "keep" the events around. That way if log sinks other that the
1579   built-in one are configured, we can flush all of the buffered
1580   events there once initialization completes.
1581   (used when log_sink_buffer() detects a time-out, see also:
1582   LOG_BUFFERING_TIMEOUT_AFTER, LOG_BUFFERING_TIMEOUT_EVERY)
1583 
1584   @param  mode  LOG_BUFFER_DISCARD_ONLY (to just
1585                 throw away the buffered events), or
1586                 LOG_BUFFER_PROCESS_AND_DISCARD to
1587                 filter/print them first, or
1588                 LOG_BUFFER_REPORT_AND_KEEP to print
1589                 an intermediate report on time-out.
1590 
1591                 To use LOG_BUFFER_REPORT_AND_KEEP in a
1592                 multi-threaded environment, the caller
1593                 needs to hold THR_LOCK_log_buffered
1594                 while calling this; for the other modes,
1595                 this function will acquire and release the
1596                 lock as needed; in this second scenario,
1597                 the lock will not be held while calling
1598                 log_services (via log_line_submit()).
1599 */
log_sink_buffer_flush(enum log_sink_buffer_flush_mode mode)1600 void log_sink_buffer_flush(enum log_sink_buffer_flush_mode mode) {
1601   log_line_buffer *llp, *local_head, *local_tail = nullptr;
1602 
1603   /*
1604     "steal" public list of buffered log events
1605 
1606     The general mechanism is that we move the buffered events from
1607     the global list to one local to this function, iterate over
1608     it, and then put it back. If anything was added to the global
1609     list we emptied while were working, we append the new items
1610     from the global list to the end of the "stolen" list, and then
1611     make that union the new global list. The grand idea here is
1612     that this way, we only have to acquire a lock very briefly
1613     (while updating the global list's head and tail, once for
1614     "stealing" it, once for giving it back), rather than holding
1615     a lock the entire time, or locking each event individually,
1616     while still remaining safe if one caller starts a
1617     flush-with-print, and then another runs a flush-to-delete
1618     that might catch up and cause trouble if we neither held
1619     a lock nor stole the list.
1620   */
1621 
1622   /*
1623     If the lock hasn't been init'd yet, don't get it.
1624 
1625     Likewise don't get it in LOG_BUFFER_REPORT_AND_KEEP mode as
1626     then the caller already has it. We generally only grab the lock
1627     here when coming from log.cc's discard_error_log_messages() or
1628     flush_error_log_messages().
1629   */
1630   if (log_builtins_inited && (mode != LOG_BUFFER_REPORT_AND_KEEP))
1631     mysql_mutex_lock(&THR_LOCK_log_buffered);
1632 
1633   local_head = log_line_buffer_start;             // save list head
1634   log_line_buffer_start = nullptr;                // empty public list
1635   log_line_buffer_tail = &log_line_buffer_start;  // adjust tail of public list
1636 
1637   if (log_builtins_inited && (mode != LOG_BUFFER_REPORT_AND_KEEP))
1638     mysql_mutex_unlock(&THR_LOCK_log_buffered);
1639 
1640   // get head element from list of buffered log events
1641   llp = local_head;
1642 
1643   while (llp != nullptr) {
1644     /*
1645       Forward the buffered lines to log-writers
1646       (other than the buffered writer), unless
1647       we're in "discard" mode, in which case,
1648       we'll just throw the information away.
1649     */
1650     if (mode != LOG_BUFFER_DISCARD_ONLY) {
1651       log_service_instance *lsi = log_service_instances;
1652 
1653       // regenerate timestamp with the correct options
1654       char local_time_buff[iso8601_size];
1655       int index_buff = log_line_index_by_type(&llp->ll, LOG_ITEM_LOG_BUFFERED);
1656       int index_time = log_line_index_by_type(&llp->ll, LOG_ITEM_LOG_TIMESTAMP);
1657       ulonglong now;
1658 
1659       if (index_buff >= 0)
1660         now = llp->ll.item[index_buff].data.data_integer;
1661       else  // we failed to set a timestamp earlier (OOM?), use current time!
1662         now = my_micro_time(); /* purecov: inspected */
1663 
1664       DBUG_EXECUTE_IF("log_error_normalize", { now = 0; });
1665 
1666       make_iso8601_timestamp(local_time_buff, now, opt_log_timestamps);
1667       char *date = my_strndup(key_memory_log_error_stack, local_time_buff,
1668                               strlen(local_time_buff) + 1, MYF(0));
1669 
1670       if (date != nullptr) {
1671         if (index_time >= 0) {
1672           // release old timestamp value
1673           if (llp->ll.item[index_time].alloc & LOG_ITEM_FREE_VALUE) {
1674             my_free(const_cast<char *>(
1675                 llp->ll.item[index_time].data.data_string.str));
1676           }
1677           // set new timestamp value
1678           llp->ll.item[index_time].data.data_string.str = date;
1679           llp->ll.item[index_time].data.data_string.length = strlen(date);
1680           llp->ll.item[index_time].alloc |= LOG_ITEM_FREE_VALUE;
1681         } else if (!log_line_full(&llp->ll)) { /* purecov: begin inspected */
1682           // set all new timestamp key/value pair; we didn't previously have one
1683           // This shouldn't happen unless we run out of space during submit()!
1684           log_item_data *d =
1685               log_line_item_set(&llp->ll, LOG_ITEM_LOG_TIMESTAMP);
1686           if (d != nullptr) {
1687             d->data_string.str = local_time_buff;
1688             d->data_string.length = strlen(d->data_string.str);
1689             llp->ll.item[llp->ll.count].alloc |= LOG_ITEM_FREE_VALUE;
1690             llp->ll.seen |= LOG_ITEM_LOG_TIMESTAMP;
1691           } else
1692             my_free(date);  // couldn't create key/value pair for timestamp
1693         } else
1694           my_free(date);  // log_line is full
1695       }                   /* purecov: end */
1696 
1697       /*
1698         If no log-service is configured (because log_builtins_init()
1699         hasn't run and initialized the log-pipeline), or if the
1700         log-service is the buffered writer (which is only available
1701         internally and used during start-up), it's still early in the
1702         start-up sequence, so we may not have all configured external
1703         log-components yet. Therefore, and since this is a fallback
1704         only, we ignore the configuration and use the built-in services
1705         that we know are available, on the grounds that when something
1706         goes wrong, information with undesired formatting is still
1707         better than not knowing about the issue at all.
1708       */
1709       if ((lsi == nullptr) || (lsi->sce->chistics & LOG_SERVICE_BUFFER)) {
1710         /*
1711           This is a fallback used when start-up takes very long.
1712 
1713           In fallback modes, we run with default settings and
1714           services.
1715 
1716           In "keep" mode (that is, "start-up's taking a long time,
1717           so show the user the info so far in the basic format, but
1718           keep the log-events so we can log them properly later"),
1719           we expect to be able to log the events with the correct
1720           settings and services later (if start-up gets that far).
1721           As the services we use in the meantime may change or even
1722           remove log events, we'll let the services work on a
1723           temporary copy of the events here. The final logging will
1724           then use the original event. This way, no information is
1725           lost.
1726         */
1727         if (mode == LOG_BUFFER_REPORT_AND_KEEP) {
1728           log_line temp_line;  // copy the line so the filter may mangle it
1729 
1730           log_line_duplicate(&temp_line, &llp->ll);
1731 
1732           // Only run the built-in filter if any rules are defined.
1733           if (log_filter_builtin_rules != nullptr)
1734             log_builtins_filter_run(log_filter_builtin_rules, &temp_line);
1735 
1736           // Emit to the built-in writer. Empty lines will be ignored.
1737           log_sink_trad(nullptr, &temp_line);
1738 
1739           log_line_item_free_all(&temp_line);  // release our temporary copy
1740 
1741           local_tail = llp;
1742           llp = llp->next;  // go to next element, keep head pointer the same
1743           continue;         // skip the free()-ing
1744 
1745         } else {  // LOG_BUFFER_PROCESS_AND_DISCARD
1746           /*
1747             This is a fallback used primarily when start-up is aborted.
1748 
1749             If we get here, flush_error_log_messages() was called
1750             before logging came out of buffered mode. (If it was
1751             called after buffered modes completes, we should land
1752             in the following branch instead.)
1753 
1754             We're asked to print all log-info so far using basic
1755             logging, and to then throw it away (rather than keep
1756             it around for proper logging). This usually implies
1757             that we're shutting down because some unrecoverable
1758             situation has arisen during start-up, so a) the user
1759             needs to know about it even if full logging (as
1760             configured) is not available yet, and b) we'll shut
1761             down before we'll ever get full logging, so keeping
1762             the info around is pointless.
1763           */
1764 
1765           if (log_filter_builtin_rules != nullptr)
1766             log_builtins_filter_run(log_filter_builtin_rules, &llp->ll);
1767 
1768           log_sink_trad(nullptr, &llp->ll);
1769         }
1770       } else {  // !LOG_SERVICE_BUFFER
1771         /*
1772           If we get here, logging has left the buffered phase, and
1773           we can write out the log-events using the configuration
1774           requested by the user, as it should be!
1775         */
1776         log_line_submit(&llp->ll);  // frees keys + values (but not llp itself)
1777         goto kv_freed;
1778       }
1779     }
1780 
1781     log_line_item_free_all(&local_head->ll);  // free key/value pairs
1782   kv_freed:
1783     local_head = local_head->next;  // delist event
1784     my_free(llp);                   // free buffered event
1785     llp = local_head;
1786   }
1787 
1788   if (log_builtins_inited && (mode != LOG_BUFFER_REPORT_AND_KEEP))
1789     mysql_mutex_lock(&THR_LOCK_log_buffered);
1790 
1791   /*
1792     At this point, if (local_head == nullptr), we either didn't have
1793     a list to begin with, or we just emptied the local version.
1794     Since we also emptied the global version at the top, whatever's
1795     in there now (still empty, or new events attached while we were
1796     processing) is now authoritive, and no change is needed here.
1797 
1798     If local_head is non-NULL, we started with a non-empty local list
1799     and mode was KEEP. In that case, we merge the local list back into
1800     the global one:
1801   */
1802 
1803   if (local_head != nullptr) {
1804     DBUG_ASSERT(local_tail != nullptr);
1805 
1806     /*
1807       Append global list to end of local list. If global list is still
1808       empty, the global tail pointer points at the global anchor, so
1809       we set the global tail pointer to the next-pointer in last stolen
1810       item. If global list was non-empty, the tail pointer already
1811       points at the correct element's next-pointer (the correct element
1812       being the last one in the global list, as we'll append the
1813       global list to the end of the local list).
1814     */
1815     if ((local_tail->next = log_line_buffer_start) == nullptr)
1816       log_line_buffer_tail = &local_tail->next;
1817 
1818     /*
1819       Return the stolen list, with any log-events that happened while
1820       we were processing appended to its end.
1821     */
1822     log_line_buffer_start = local_head;
1823   }
1824 
1825   if (log_builtins_inited && (mode != LOG_BUFFER_REPORT_AND_KEEP))
1826     mysql_mutex_unlock(&THR_LOCK_log_buffered);
1827 }
1828 
1829 /**
1830   Complete, filter, and write submitted log items.
1831 
1832   This expects a log_line collection of log-related key/value pairs,
1833   e.g. from log_message().
1834 
1835   Where missing, timestamp, priority, thread-ID (if any) and so forth
1836   are added.
1837 
1838   Log item source services, log item filters, and log item sinks are
1839   then called.
1840 
1841   @param           ll                   key/value pairs describing info to log
1842 
1843   @retval          int                  number of fields in created log line
1844 */
log_line_submit(log_line * ll)1845 int log_line_submit(log_line *ll) {
1846   log_item_iter iter_save;
1847 
1848   DBUG_TRACE;
1849 
1850   /*
1851     The log-services we'll call below are likely to change the default
1852     iter. Since log-services are allowed to call the logger, we'll save
1853     the iter on entry and restore it on exit to be properly re-entrant in
1854     that regard.
1855   */
1856   iter_save = ll->iter;
1857   ll->iter.ll = nullptr;
1858 
1859   /*
1860     If anything of what was submitted survived, proceed ...
1861   */
1862   if (ll->count > 0) {
1863     THD *thd = nullptr;
1864 
1865     // avoid some allocs/frees.
1866     char local_time_buff[iso8601_size];
1867     char strerr_buf[MYSYS_STRERROR_SIZE];
1868 
1869     /* auto-add a prio item */
1870     if (!(ll->seen & LOG_ITEM_LOG_PRIO) && !log_line_full(ll)) {
1871       log_line_item_set(ll, LOG_ITEM_LOG_PRIO)->data_integer = ERROR_LEVEL;
1872     }
1873 
1874     /* auto-add a timestamp item if needed */
1875     if (!(ll->seen & LOG_ITEM_LOG_TIMESTAMP) && !log_line_full(ll)) {
1876       log_item_data *d;
1877       ulonglong now = my_micro_time();
1878 
1879       DBUG_EXECUTE_IF("log_error_normalize", { now = 0; });
1880 
1881       make_iso8601_timestamp(local_time_buff, now, opt_log_timestamps);
1882 
1883       d = log_line_item_set(ll, LOG_ITEM_LOG_TIMESTAMP);
1884       d->data_string.str = local_time_buff;
1885       d->data_string.length = strlen(d->data_string.str);
1886     }
1887 
1888     /* auto-add a ts item if needed */
1889     if (!(ll->seen & LOG_ITEM_LOG_TS) && !log_line_full(ll)) {
1890       log_item_data *d;
1891       ulonglong now = my_milli_time();
1892 
1893       DBUG_EXECUTE_IF("log_error_normalize", { now = 0; });
1894 
1895       d = log_line_item_set(ll, LOG_ITEM_LOG_TS);
1896       d->data_integer = now;
1897     }
1898 
1899     /* auto-add a strerror item if relevant and available */
1900     if (!(ll->seen & LOG_ITEM_SYS_STRERROR) && !log_line_full(ll) &&
1901         (ll->seen & LOG_ITEM_SYS_ERRNO)) {
1902       int en;  // operating system errno
1903       int n = log_line_index_by_type(ll, LOG_ITEM_SYS_ERRNO);
1904       log_item_data *d = log_line_item_set(ll, LOG_ITEM_SYS_STRERROR);
1905 
1906       DBUG_ASSERT(n >= 0);
1907 
1908       en = (int)ll->item[n].data.data_integer;
1909       my_strerror(strerr_buf, sizeof(strerr_buf), en);
1910       d->data_string.str = strerr_buf;
1911       d->data_string.length = strlen(d->data_string.str);
1912     }
1913 
1914     /* add thread-related info, if available */
1915     if ((thd = current_thd) != nullptr) {
1916       /* auto-add a thread item if needed */
1917       if (!(ll->seen & LOG_ITEM_SRV_THREAD) && !log_line_full(ll)) {
1918         my_thread_id tid = log_get_thread_id(thd);
1919 
1920         DBUG_EXECUTE_IF("log_error_normalize", { tid = 0; });
1921 
1922         log_line_item_set(ll, LOG_ITEM_SRV_THREAD)->data_integer = tid;
1923       }
1924     }
1925 
1926     /* auto-add a symbolic MySQL error code item item if needed */
1927     if (!(ll->seen & LOG_ITEM_SQL_ERRSYMBOL) && !log_line_full(ll) &&
1928         (ll->seen & LOG_ITEM_SQL_ERRCODE)) {
1929       int ec;  // MySQL error code
1930       int n = log_line_index_by_type(ll, LOG_ITEM_SQL_ERRCODE);
1931       const char *es;
1932 
1933       DBUG_ASSERT(n >= 0);
1934 
1935       ec = (int)ll->item[n].data.data_integer;
1936       if ((ec != 0) && ((es = mysql_errno_to_symbol(ec)) != nullptr)) {
1937         log_item_data *d = log_line_item_set(ll, LOG_ITEM_SQL_ERRSYMBOL);
1938         d->data_string.str = es;
1939         d->data_string.length = strlen(d->data_string.str);
1940       }
1941     }
1942     /* auto-add a numeric MySQL error code item item if needed */
1943     else if (!(ll->seen & LOG_ITEM_SQL_ERRCODE) && !log_line_full(ll) &&
1944              (ll->seen & LOG_ITEM_SQL_ERRSYMBOL)) {
1945       const char *es;  // MySQL error symbol
1946       int n = log_line_index_by_type(ll, LOG_ITEM_SQL_ERRSYMBOL);
1947       int ec;
1948 
1949       DBUG_ASSERT(n >= 0);
1950 
1951       es = ll->item[n].data.data_string.str;
1952 
1953       DBUG_ASSERT(es != nullptr);
1954 
1955       if ((ec = mysql_symbol_to_errno(es)) > 0) {
1956         log_item_data *d = log_line_item_set(ll, LOG_ITEM_SQL_ERRCODE);
1957         d->data_integer = ec;
1958       }
1959     }
1960 
1961     /* auto-add a SQL state item item if needed */
1962     if (!(ll->seen & LOG_ITEM_SQL_STATE) && !log_line_full(ll) &&
1963         (ll->seen & LOG_ITEM_SQL_ERRCODE)) {
1964       int ec;  // MySQL error code
1965       int n = log_line_index_by_type(ll, LOG_ITEM_SQL_ERRCODE);
1966       const char *es;
1967 
1968       if (n < 0) {
1969         n = log_line_index_by_type(ll, LOG_ITEM_SQL_ERRSYMBOL);
1970         DBUG_ASSERT(n >= 0);
1971 
1972         es = ll->item[n].data.data_string.str;
1973         DBUG_ASSERT(es != nullptr);
1974 
1975         ec = mysql_symbol_to_errno(es);
1976       } else
1977         ec = (int)ll->item[n].data.data_integer;
1978 
1979       if ((ec > 0) && ((es = mysql_errno_to_sqlstate((uint)ec)) != nullptr)) {
1980         log_item_data *d = log_line_item_set(ll, LOG_ITEM_SQL_STATE);
1981         d->data_string.str = es;
1982         d->data_string.length = strlen(d->data_string.str);
1983       }
1984     }
1985 
1986     /* add the default sub-system if none is set */
1987     if (!(ll->seen & LOG_ITEM_SRV_SUBSYS) && !log_line_full(ll)) {
1988       log_item_data *d = log_line_item_set(ll, LOG_ITEM_SRV_SUBSYS);
1989       d->data_string.str = LOG_SUBSYSTEM_TAG;
1990       d->data_string.length = strlen(d->data_string.str);
1991     }
1992 
1993     /* normalize source line if needed */
1994     DBUG_EXECUTE_IF("log_error_normalize", {
1995       if (ll->seen & LOG_ITEM_SRC_LINE) {
1996         int n = log_line_index_by_type(ll, LOG_ITEM_SRC_LINE);
1997 
1998         if (n >= 0) {
1999           ll->item[n] = ll->item[ll->count - 1];
2000           ll->count--;
2001           ll->seen &= ~LOG_ITEM_SRC_LINE;
2002         }
2003       }
2004     });
2005 
2006     /*
2007       We were called before even the buffered sink (and our locks)
2008       were set up. This usually means that something went
2009       catastrophically wrong, so we'll make sure the information
2010       (e.g. cause of failure) isn't lost.
2011     */
2012     if (!log_builtins_inited)
2013       log_sink_buffer(nullptr, ll);
2014 
2015     else {
2016       mysql_rwlock_rdlock(&THR_LOCK_log_stack);
2017 
2018       /*
2019         sources:
2020         Add info from other log item sources,
2021         e.g. that supplied by the client on connect using mysql_options4();
2022 
2023         filters:
2024         Remove or modify entries
2025 
2026         sinks:
2027         Write logs
2028       */
2029 
2030       log_service_cache_entry *sce;
2031       log_service_instance *lsi = log_service_instances;
2032 
2033       while ((lsi != nullptr) && ((sce = lsi->sce) != nullptr)) {
2034         if (sce->chistics & LOG_SERVICE_BUFFER)
2035           log_sink_buffer(lsi->instance, ll);
2036 
2037         else if (!(sce->chistics & LOG_SERVICE_BUILTIN)) {
2038           SERVICE_TYPE(log_service) * ls;
2039 
2040           ls = reinterpret_cast<SERVICE_TYPE(log_service) *>(sce->service);
2041 
2042           if (ls != nullptr) ls->run(lsi->instance, ll);
2043 
2044         } else if (log_service_has_characteristics(
2045                        sce, (LOG_SERVICE_BUILTIN | LOG_SERVICE_FILTER)))
2046           log_builtins_filter_run(log_filter_builtin_rules, ll);
2047 
2048         else if (log_service_has_characteristics(
2049                      sce, (LOG_SERVICE_BUILTIN | LOG_SERVICE_SINK)))
2050           log_sink_trad(lsi->instance, ll);
2051 
2052         lsi = lsi->next;
2053       }
2054 
2055       mysql_rwlock_unlock(&THR_LOCK_log_stack);
2056     }
2057 
2058 #if !defined(DBUG_OFF)
2059     /*
2060       Assert that we're not given anything but server error-log codes
2061       or global error codes (shared between MySQL server and clients).
2062       If your code bombs out here, check whether you're trying to log
2063       using an error-code in the range intended for messages that are
2064       sent to the client, not the error-log, (< ER_SERVER_RANGE_START).
2065     */
2066     if (ll->seen & LOG_ITEM_SQL_ERRCODE) {
2067       int n = log_line_index_by_type(ll, LOG_ITEM_SQL_ERRCODE);
2068       if (n >= 0) {
2069         int ec = (int)ll->item[n].data.data_integer;
2070         DBUG_ASSERT((ec < 1) || (ec >= EE_ERROR_FIRST && ec <= EE_ERROR_LAST) ||
2071                     (ec >= ER_SERVER_RANGE_START));
2072       }
2073     }
2074 #endif
2075 
2076     // release any memory that might need it
2077     log_line_item_free_all(ll);
2078   }
2079 
2080   ll->iter = iter_save;
2081 
2082   return ll->count;
2083 }
2084 
2085 /**
2086   Make and return an ISO 8601 / RFC 3339 compliant timestamp.
2087   Heeds log_timestamps.
2088 
2089   @param buf       A buffer of at least 26 bytes to store the timestamp in
2090                    (19 + tzinfo tail + \0)
2091   @param utime     Microseconds since the epoch
2092   @param mode      if 0, use UTC; if 1, use local time
2093 
2094   @retval          length of timestamp (excluding \0)
2095 */
make_iso8601_timestamp(char * buf,ulonglong utime,int mode)2096 int make_iso8601_timestamp(char *buf, ulonglong utime, int mode) {
2097   struct tm my_tm;
2098   char tzinfo[8] = "Z";  // max 6 chars plus \0
2099   size_t len;
2100   time_t seconds;
2101 
2102   seconds = utime / 1000000;
2103   utime = utime % 1000000;
2104 
2105   if (mode == 0)
2106     gmtime_r(&seconds, &my_tm);
2107   else if (mode == 1) {
2108     localtime_r(&seconds, &my_tm);
2109 
2110 #ifdef HAVE_TM_GMTOFF
2111     /*
2112       The field tm_gmtoff is the offset (in seconds) of the time represented
2113       from UTC, with positive values indicating east of the Prime Meridian.
2114       Originally a BSDism, this is also supported in glibc, so this should
2115       cover the majority of our platforms.
2116     */
2117     long tim = -my_tm.tm_gmtoff;
2118 #else
2119     /*
2120       Work this out "manually".
2121     */
2122     struct tm my_gm;
2123     long tim, gm;
2124     gmtime_r(&seconds, &my_gm);
2125     gm = (my_gm.tm_sec + 60 * (my_gm.tm_min + 60 * my_gm.tm_hour));
2126     tim = (my_tm.tm_sec + 60 * (my_tm.tm_min + 60 * my_tm.tm_hour));
2127     tim = gm - tim;
2128 #endif
2129     char dir = '-';
2130 
2131     if (tim < 0) {
2132       dir = '+';
2133       tim = -tim;
2134     }
2135     snprintf(tzinfo, sizeof(tzinfo), "%c%02u:%02u", dir,
2136              (unsigned int)((tim / (60 * 60)) % 100),
2137              (unsigned int)((tim / 60) % 60));
2138   } else {
2139     DBUG_ASSERT(false);
2140   }
2141 
2142   len = snprintf(buf, iso8601_size, "%04d-%02d-%02dT%02d:%02d:%02d.%06lu%s",
2143                  my_tm.tm_year + 1900, my_tm.tm_mon + 1, my_tm.tm_mday,
2144                  my_tm.tm_hour, my_tm.tm_min, my_tm.tm_sec,
2145                  (unsigned long)utime, tzinfo);
2146 
2147   return std::min<int>((int)len, iso8601_size - 1);
2148 }
2149 
2150 /**
2151   Helper: get token from error stack configuration string
2152 
2153   @param[in,out]  s   start of the token (may be positioned on whitespace
2154                       on call; this will be adjusted to the first non-white
2155                       character)
2156   @param[out]     e   end of the token
2157   @param[in,out]  d   delimiter (in: last used, \0 if none; out: detected here)
2158 
2159   @retval         <0  an error occur
2160   @retval            the length in bytes of the token
2161 */
log_builtins_stack_get_service_from_var(const char ** s,const char ** e,char * d)2162 static size_t log_builtins_stack_get_service_from_var(const char **s,
2163                                                       const char **e, char *d) {
2164   DBUG_ASSERT(s != nullptr);
2165   DBUG_ASSERT(e != nullptr);
2166 
2167   // proceed to next service (skip whitespace, and the delimiter once defined)
2168   while (isspace(**s) || ((*d != '\0') && (**s == *d))) (*s)++;
2169 
2170   *e = *s;
2171 
2172   // find end of service
2173   while ((**e != '\0') && !isspace(**e)) {
2174     if ((**e == ';') || (**e == ',')) {
2175       if (*d == '\0')  // no delimiter determined yet
2176       {
2177         if (*e == *s)  // token may not start with a delimiter
2178           return -1;
2179         *d = **e;            // save the delimiter we found
2180       } else if (**e != *d)  // different delimiter than last time: error
2181         return -2;
2182     }
2183     if (**e == *d)  // found a valid delimiter; end scan
2184       goto done;
2185     (*e)++;  // valid part of token found, go on!
2186   }
2187 
2188 done:
2189   return (size_t)(*e - *s);
2190 }
2191 
2192 /**
2193   Look up a log service by name (in the service registry).
2194 
2195   @param        name    name of the component
2196   @param        len     length of that name
2197 
2198   @retval               a handle to that service.
2199 */
log_service_get_by_name(const char * name,size_t len)2200 static my_h_service log_service_get_by_name(const char *name, size_t len) {
2201   char buf[128];
2202   my_h_service service = nullptr;
2203   size_t needed;
2204 
2205   needed =
2206       snprintf(buf, sizeof(buf), LOG_SERVICES_PREFIX ".%.*s", (int)len, name);
2207 
2208   if (needed > sizeof(buf)) return service;
2209 
2210   if ((!srv_registry->acquire(buf, &service)) && (service != nullptr)) {
2211     SERVICE_TYPE(log_service) * ls;
2212 
2213     if ((ls = reinterpret_cast<SERVICE_TYPE(log_service) *>(service)) ==
2214         nullptr) {
2215       srv_registry->release(service);
2216       service = nullptr;
2217     }
2218   } else
2219     service = nullptr;
2220 
2221   return service;
2222 }
2223 
operator ()(log_service_cache_entry * sce) const2224 void log_service_cache_entry_free::operator()(
2225     log_service_cache_entry *sce) const {
2226   if (sce == nullptr) return;
2227 
2228   if (sce->name != nullptr) my_free(sce->name);
2229 
2230   DBUG_ASSERT(sce->opened == 0);
2231 
2232   if (sce->service != nullptr) srv_registry->release(sce->service);
2233 
2234   memset(sce, 0, sizeof(log_service_cache_entry));
2235 
2236   my_free(sce);
2237 }
2238 
2239 /**
2240   Create a new entry in the cache of log services.
2241 
2242   @param  name      Name of component that provides the service
2243   @param  name_len  Length of that name
2244   @param  srv       The handle of the log_service
2245 
2246   @retval           A new log_service_cache_entry on success
2247   @retval           nullptr on failure
2248 */
log_service_cache_entry_new(const char * name,size_t name_len,my_h_service srv)2249 static log_service_cache_entry *log_service_cache_entry_new(const char *name,
2250                                                             size_t name_len,
2251                                                             my_h_service srv) {
2252   char *n = my_strndup(key_memory_log_error_stack, name, name_len, MYF(0));
2253   log_service_cache_entry *sce = nullptr;
2254 
2255   if (n != nullptr) {
2256     // make new service cache entry
2257     if ((sce = (log_service_cache_entry *)my_malloc(
2258              key_memory_log_error_stack, sizeof(log_service_cache_entry),
2259              MYF(0))) == nullptr)
2260       my_free(n);
2261     else {
2262       memset(sce, 0, sizeof(log_service_cache_entry));
2263       sce->name = n;
2264       sce->name_len = name_len;
2265       sce->service = srv;
2266       sce->chistics = LOG_SERVICE_UNSPECIFIED;
2267       sce->requested = 0;
2268       sce->opened = 0;
2269     }
2270   }
2271 
2272   return sce;
2273 }
2274 
2275 /**
2276   Find out characteristics of a service (e.g. whether it is a singleton)
2277   by asking it.
2278 
2279   (See log_service_chistics for a list of possible characteristics!)
2280 
2281   @param  service  what service to examine
2282 
2283   @retval a set of log_service_chistics flags
2284 */
log_service_get_characteristics(my_h_service service)2285 static int log_service_get_characteristics(my_h_service service) {
2286   SERVICE_TYPE(log_service) * ls;
2287 
2288   DBUG_ASSERT(service != nullptr);
2289 
2290   ls = reinterpret_cast<SERVICE_TYPE(log_service) *>(service);
2291 
2292   // no information available, default to restrictive
2293   if (ls->characteristics == nullptr)
2294     return LOG_SERVICE_UNSPECIFIED |
2295            LOG_SERVICE_SINGLETON; /* purecov: inspected */
2296 
2297   return ls->characteristics();
2298 }
2299 
2300 /**
2301   Allocate and open a new instance of a given service.
2302 
2303   @param  sce  the cache-entry for the service
2304   @param  ll   a log_line containing optional parameters, or nullptr
2305 
2306   @return      a pointer to an instance record or success, nullptr otherwise
2307 */
log_service_instance_new(log_service_cache_entry * sce,log_line * ll)2308 log_service_instance *log_service_instance_new(log_service_cache_entry *sce,
2309                                                log_line *ll) {
2310   log_service_instance *lsi;
2311 
2312   // make new service instance entry
2313   if ((lsi = (log_service_instance *)my_malloc(
2314            key_memory_log_error_stack, sizeof(log_service_instance), MYF(0))) !=
2315       nullptr) {
2316     memset(lsi, 0, sizeof(log_service_instance));
2317     lsi->sce = sce;
2318 
2319     DBUG_ASSERT(sce != nullptr);
2320 
2321     if (lsi->sce->service != nullptr) {
2322       SERVICE_TYPE(log_service) *ls = nullptr;
2323 
2324       ls = reinterpret_cast<SERVICE_TYPE(log_service) *>(lsi->sce->service);
2325 
2326       if ((ls == nullptr) ||
2327           ((ls->open != nullptr) && (ls->open(ll, &lsi->instance) < 0)))
2328         goto fail;
2329     }
2330 
2331     lsi->sce->opened++;
2332   }
2333 
2334   return lsi;
2335 
2336 fail:
2337   my_free(lsi);
2338   return nullptr;
2339 }
2340 
2341 /**
2342   Close and release all instances of all log services.
2343 */
log_service_instance_release_all()2344 static void log_service_instance_release_all() {
2345   log_service_instance *lsi, *lsi_next;
2346 
2347   lsi = log_service_instances;
2348   log_service_instances = nullptr;
2349 
2350   // release all instances!
2351   while (lsi != nullptr) {
2352     SERVICE_TYPE(log_service) * ls;
2353 
2354     ls = reinterpret_cast<SERVICE_TYPE(log_service) *>(lsi->sce->service);
2355 
2356     if (ls != nullptr) {
2357       if (ls->close != nullptr) ls->close(&lsi->instance);
2358     }
2359 
2360     lsi->sce->opened--;
2361     lsi_next = lsi->next;
2362     my_free(lsi);
2363     lsi = lsi_next;
2364   }
2365 }
2366 
2367 /**
2368   Call flush() on all log_services.
2369   flush() function must not try to log anything, as we hold an
2370   exclusive lock on the stack.
2371 
2372   @retval   0   no problems
2373   @retval  -1   error
2374 */
log_builtins_error_stack_flush()2375 int log_builtins_error_stack_flush() {
2376   int rr = 0;
2377   log_service_cache_entry *sce;
2378   log_service_instance *lsi;
2379 
2380   if (!log_builtins_inited) return 0;
2381 
2382   mysql_rwlock_wrlock(&THR_LOCK_log_stack);
2383 
2384   lsi = log_service_instances;
2385 
2386   while ((lsi != nullptr) && ((sce = lsi->sce) != nullptr)) {
2387     if (!(sce->chistics & LOG_SERVICE_BUILTIN)) {
2388       SERVICE_TYPE(log_service) *ls = nullptr;
2389       ls = reinterpret_cast<SERVICE_TYPE(log_service) *>(sce->service);
2390 
2391       if (ls != nullptr) {
2392         if ((ls->flush != nullptr) && (ls->flush(&lsi->instance) < 0)) rr--;
2393       } else
2394         rr--;
2395     }
2396     lsi = lsi->next;
2397   }
2398 
2399   mysql_rwlock_unlock(&THR_LOCK_log_stack);
2400 
2401   return rr;
2402 }
2403 
2404 /**
2405   Set up custom error logging stack.
2406 
2407   @param        conf        The configuration string
2408   @param        check_only  If true, report on whether configuration is valid
2409                             (i.e. whether all requested services are available),
2410                             but do not apply the new configuration.
2411                             if false, set the configuration (acquire the
2412                             necessary services, update the hash by
2413                             adding/deleting entries as necessary)
2414   @param[out]   pos         If an error occurs and this pointer is non-null,
2415                             the position in the configuration string where
2416                             the error occurred will be written to the
2417                             pointed-to size_t.
2418 
2419   @retval              0    success
2420   @retval             -1    expected delimiter not found
2421   @retval             -2    one or more services not found
2422   @retval             -3    failed to create service cache entry
2423   @retval             -4    tried to open multiple instances of a singleton
2424   @retval             -5    failed to create service instance entry
2425   @retval             -6    last element in pipeline should be a sink
2426   @retval             -7    service only available during start-up;
2427                             may not be set by the user
2428   @retval             -101  service name may not start with a delimiter
2429   @retval             -102  delimiters ',' and ';' may not be mixed
2430 */
log_builtins_error_stack(const char * conf,bool check_only,size_t * pos)2431 int log_builtins_error_stack(const char *conf, bool check_only, size_t *pos) {
2432   const char *start = conf, *end;
2433   char delim = '\0';
2434   long len;
2435   my_h_service service;
2436   int rr = 0;
2437   int count = 0;
2438   log_service_cache_entry *sce = nullptr;
2439   log_service_instance *lsi;
2440   int chistics = LOG_SERVICE_UNSPECIFIED;
2441 
2442   mysql_rwlock_wrlock(&THR_LOCK_log_stack);
2443 
2444   // if we're actually setting this configuration, release the previous one!
2445   if (!check_only) log_service_instance_release_all();
2446 
2447   // clear keep flag on all service cache entries
2448   for (auto &key_and_value : *log_service_cache) {
2449     sce = key_and_value.second.get();
2450     sce->requested = 0;
2451 
2452     DBUG_ASSERT(check_only || (sce->opened == 0));
2453   }
2454 
2455   sce = nullptr;
2456 
2457   lsi = nullptr;
2458   while ((len = log_builtins_stack_get_service_from_var(&start, &end, &delim)) >
2459          0) {
2460     chistics = LOG_SERVICE_UNSPECIFIED;
2461 
2462     // more than one services listed, but no delimiter used (only space)
2463     if ((++count > 1) && (delim == '\0')) {
2464       rr = -1;  // at least one service not found => fail
2465       goto done;
2466     }
2467 
2468     // find current service name in service-cache
2469     auto it = log_service_cache->find(string(start, len));
2470 
2471     // not found in cache; is it a built-in default?
2472     if (it == log_service_cache->end()) {
2473       chistics = log_service_check_if_builtin(start, len);
2474 
2475       /*
2476         Buffered logging is only used during start-up.
2477         As we set it up internally from a constant, we don't
2478         check the value first, but go straight to the set phase.
2479         If we do see the special sink during the check phase,
2480         it was requested by the user. That is not supported
2481         (as it isn't useful), so we throw an error here.
2482       */
2483       if (check_only && (chistics & LOG_SERVICE_BUFFER)) {
2484         rr = -7;
2485         goto done;
2486       }
2487 
2488       // not a built-in; ask component framework
2489       if (!(chistics & LOG_SERVICE_BUILTIN)) {
2490         service = log_service_get_by_name(start, len);
2491 
2492         // not found in framework, signal failure
2493         if (service == nullptr) {
2494           rr = -2;  // at least one service not found => fail
2495           goto done;
2496         }
2497       } else
2498         service = nullptr;
2499 
2500       // make a cache-entry for this service
2501       if ((sce = log_service_cache_entry_new(start, len, service)) == nullptr) {
2502         // failed to make cache-entry. if we hold a service handle, release it!
2503         /* purecov: begin inspected */
2504         if (service != nullptr) srv_registry->release(service);
2505         rr = -3;
2506         goto done; /* purecov: end */
2507       }
2508 
2509       // service is not built-in, so we know nothing about it. Ask it!
2510       if ((sce->chistics = chistics) == LOG_SERVICE_UNSPECIFIED)
2511         sce->chistics =
2512             log_service_get_characteristics(service) &
2513             ~LOG_SERVICE_BUILTIN;  // loaded service can not be built-in
2514 
2515       log_service_cache->emplace(string(sce->name, sce->name_len),
2516                                  cache_entry_with_deleter(sce));
2517     } else
2518       sce = it->second.get();
2519 
2520     // at this point, it's in cache, one way or another
2521     sce->requested++;
2522 
2523     if (check_only) {
2524       // tried to multi-open a service that doesn't support it => fail
2525       if ((sce->requested > 1) && (sce->chistics & LOG_SERVICE_SINGLETON)) {
2526         rr = -4;
2527         goto done;
2528       }
2529     } else if ((sce->requested == 1) ||
2530                !(sce->chistics & LOG_SERVICE_SINGLETON)) {
2531       log_service_instance *lsi_new = nullptr;
2532 
2533       // actually setting this config, so open this instance!
2534       lsi_new = log_service_instance_new(sce, nullptr);
2535 
2536       if (lsi_new != nullptr)  // add to chain of instances
2537       {
2538         if (log_service_instances == nullptr)
2539           log_service_instances = lsi_new;
2540         else {
2541           DBUG_ASSERT(lsi != nullptr);
2542           lsi->next = lsi_new;
2543         }
2544 
2545         lsi = lsi_new;
2546       } else  // could not make new instance entry; fail
2547       {
2548         rr = -5;   /* purecov: inspected */
2549         goto done; /* purecov: inspected */
2550       }
2551     }
2552 
2553     /*
2554       If neither branch was true, we're in set mode, but the set-up
2555       is invalid (i.e. we're trying to multi-open a singleton). As
2556       this should have been caught in the check phase, we don't
2557       specfically handle it here; the invalid element is skipped and
2558       not added to the instance list; that way, we'll get as close
2559       to a working configuration as possible in our attempt to fail
2560       somewhat gracefully.
2561     */
2562 
2563     start = end;
2564   }
2565 
2566   if (len < 0)
2567     rr = len - 100;
2568   else if ((sce != nullptr) && !(sce->chistics & LOG_SERVICE_SINK))
2569     rr = -6;
2570   else
2571     rr = 0;
2572 
2573 done:
2574   // remove stale entries from cache
2575   for (auto it = log_service_cache->begin(); it != log_service_cache->end();) {
2576     sce = it->second.get();
2577 
2578     if (sce->opened <= 0)
2579       it = log_service_cache->erase(it);
2580     else
2581       ++it;
2582   }
2583 
2584   mysql_rwlock_unlock(&THR_LOCK_log_stack);
2585 
2586   if (pos != nullptr) *pos = (size_t)(start - conf);
2587 
2588   return rr;
2589 }
2590 
2591 /**
2592   De-initialize the structured logging subsystem.
2593 
2594   @retval  0  no errors
2595   @retval -1  not stopping, never started
2596 */
log_builtins_exit()2597 int log_builtins_exit() {
2598   if (!log_builtins_inited) return -1;
2599 
2600   mysql_rwlock_wrlock(&THR_LOCK_log_stack);
2601   mysql_mutex_lock(&THR_LOCK_log_buffered);
2602   mysql_mutex_lock(&THR_LOCK_log_syseventlog);
2603 
2604   log_builtins_filter_exit();
2605   log_service_instance_release_all();
2606   delete log_service_cache;
2607 
2608   log_builtins_inited = false;
2609   log_error_stage_set(LOG_ERROR_STAGE_BUFFERING_EARLY);
2610 
2611   mysql_rwlock_unlock(&THR_LOCK_log_stack);
2612   mysql_rwlock_destroy(&THR_LOCK_log_stack);
2613 
2614   mysql_mutex_unlock(&THR_LOCK_log_syseventlog);
2615   mysql_mutex_destroy(&THR_LOCK_log_syseventlog);
2616 
2617   mysql_mutex_unlock(&THR_LOCK_log_buffered);
2618   mysql_mutex_destroy(&THR_LOCK_log_buffered);
2619 
2620   return 0;
2621 }
2622 
2623 /**
2624   Initialize the structured logging subsystem.
2625 
2626   Since we're initializing various locks here, we must call this late enough
2627   so this is clean, but early enough so it still happens while we're running
2628   single-threaded -- this specifically also means we must call it before we
2629   start plug-ins / storage engines / external components!
2630 
2631   @retval  0  no errors
2632   @retval -1  couldn't initialize stack lock
2633   @retval -2  couldn't initialize built-in default filter
2634   @retval -3  couldn't set up service hash
2635   @retval -4  couldn't initialize syseventlog lock
2636   @retval -5  couldn't set service pipeline
2637   @retval -6  couldn't initialize buffered logging lock
2638 */
log_builtins_init()2639 int log_builtins_init() {
2640   int rr = 0;
2641 
2642   DBUG_ASSERT(!log_builtins_inited);
2643 
2644   // Reset flag. This is *also* set on definition, this is intentional.
2645   log_buffering_flushworthy = false;
2646 
2647   if (mysql_rwlock_init(0, &THR_LOCK_log_stack)) return -1;
2648 
2649   if (mysql_mutex_init(0, &THR_LOCK_log_syseventlog, MY_MUTEX_INIT_FAST)) {
2650     mysql_rwlock_destroy(&THR_LOCK_log_stack);
2651     return -4;
2652   }
2653 
2654   if (mysql_mutex_init(0, &THR_LOCK_log_buffered, MY_MUTEX_INIT_FAST)) {
2655     rr = -6;
2656     goto fail;
2657   }
2658 
2659   mysql_rwlock_wrlock(&THR_LOCK_log_stack);
2660 
2661   if (log_builtins_filter_init())
2662     rr = -2;
2663   else {
2664     log_service_cache =
2665         new collation_unordered_map<string, cache_entry_with_deleter>(
2666             system_charset_info, 0);
2667     if (log_service_cache == nullptr) rr = -3;
2668   }
2669 
2670   log_service_instances = nullptr;
2671 
2672   mysql_rwlock_unlock(&THR_LOCK_log_stack);
2673 
2674   if (rr >= 0) {
2675     if (log_builtins_error_stack(LOG_BUILTINS_BUFFER, false, nullptr) >= 0) {
2676       log_error_stage_set(LOG_ERROR_STAGE_BUFFERING_EARLY);
2677       log_builtins_inited = true;
2678       return 0;
2679     } else {
2680       rr = -5;            /* purecov: inspected */
2681       DBUG_ASSERT(false); /* purecov: inspected */
2682     }
2683   }
2684 
2685 fail:
2686   mysql_rwlock_destroy(&THR_LOCK_log_stack); /* purecov: begin inspected */
2687   mysql_mutex_destroy(&THR_LOCK_log_syseventlog);
2688 
2689   return rr; /* purecov: end */
2690 }
2691 
2692 /*
2693   Service: helpers for logging. Mostly accessors for log events.
2694   See include/mysql/components/services/log_builtins.h for more information.
2695 */
2696 
2697 /**
2698   See whether a type is wellknown.
2699 
2700   @param t       log item type to examine
2701 
2702   @retval        LOG_ITEM_TYPE_NOT_FOUND: key not found
2703   @retval        >0:                      index in array of wellknowns
2704 */
2705 DEFINE_METHOD(int, log_builtins_imp::wellknown_by_type, (log_item_type t)) {
2706   return log_item_wellknown_by_type(t);
2707 }
2708 
2709 /**
2710   See whether a string is a wellknown field name.
2711 
2712   @param key     potential key starts here
2713   @param length  length of the string to examine
2714 
2715   @retval        LOG_ITEM_TYPE_RESERVED:  reserved, but not "wellknown" key
2716   @retval        LOG_ITEM_TYPE_NOT_FOUND: key not found
2717   @retval        >0:                      index in array of wellknowns
2718 */
2719 DEFINE_METHOD(int, log_builtins_imp::wellknown_by_name,
2720               (const char *key, size_t length)) {
2721   return log_item_wellknown_by_name(key, length);
2722 }
2723 
2724 /**
2725   Accessor: from a record describing a wellknown key, get its type
2726 
2727   @param i       index in array of wellknowns, see log_item_wellknown_by_...()
2728 
2729   @retval        the log item type for the wellknown key
2730 */
2731 DEFINE_METHOD(log_item_type, log_builtins_imp::wellknown_get_type, (uint i)) {
2732   return log_item_wellknown_get_type(i);
2733 }
2734 
2735 /**
2736   Accessor: from a record describing a wellknown key, get its name
2737 
2738   @param   i    index in array of wellknowns, see log_item_wellknown_by_...()
2739 
2740   @retval       name (NTBS)
2741 */
2742 DEFINE_METHOD(const char *, log_builtins_imp::wellknown_get_name, (uint i)) {
2743   return log_item_wellknown_get_name(i);
2744 }
2745 
2746 /**
2747   Sanity check an item.
2748   Certain log sinks have very low requirements with regard to the data
2749   they receive; they write keys as strings, and then data according to
2750   the item's class (string, integer, or float), formatted to the sink's
2751   standards (e.g. JSON, XML, ...).
2752   Code that has higher requirements can use this check to see whether
2753   the given item is of a known type (whether generic or wellknown),
2754   whether the given type and class agree, and whether in case of a
2755   well-known type, the given key is correct for that type.
2756   If your code generates items that don't pass this check, you should
2757   probably go meditate on it.
2758 
2759   @param  li  the log_item to check
2760 
2761   @retval  0  no problems
2762   @retval -2  unknown item type
2763   @retval -3  item_class derived from type isn't what's set on the item
2764   @retval -4  class not generic, so key should match wellknown
2765 */
2766 DEFINE_METHOD(int, log_builtins_imp::item_inconsistent, (log_item * li)) {
2767   return log_item_inconsistent(li);
2768 }
2769 
2770 /**
2771   Predicate used to determine whether a type is generic
2772   (generic string, generic float, generic integer) rather
2773   than a well-known type.
2774 
2775   @param t          log item type to examine
2776 
2777   @retval  true     if generic type
2778   @retval  false    if wellknown type
2779 */
2780 DEFINE_METHOD(bool, log_builtins_imp::item_generic_type, (log_item_type t)) {
2781   return log_item_generic_type(t);
2782 }
2783 
2784 /**
2785   Predicate used to determine whether a class is a string
2786   class (C-string or Lex-string).
2787 
2788   @param c          log item class to examine
2789 
2790   @retval   true    if of a string class
2791   @retval   false   if not of a string class
2792 */
2793 DEFINE_METHOD(bool, log_builtins_imp::item_string_class, (log_item_class c)) {
2794   return log_item_string_class(c);
2795 }
2796 
2797 /**
2798   Predicate used to determine whether a class is a numeric
2799   class (integer or float).
2800 
2801   @param c         log item class to examine
2802 
2803   @retval   true   if of a numeric class
2804   @retval   false  if not of a numeric class
2805 */
2806 DEFINE_METHOD(bool, log_builtins_imp::item_numeric_class, (log_item_class c)) {
2807   return log_item_numeric_class(c);
2808 }
2809 
2810 /**
2811   Set an integer value on a log_item.
2812   Fails gracefully if no log_item_data is supplied, so it can safely
2813   wrap log_line_item_set[_with_key]().
2814 
2815   @param  lid    log_item_data struct to set the value on
2816   @param  i      integer to set
2817 
2818   @retval true   lid was nullptr (possibly: OOM, could not set up log_item)
2819   @retval false  all's well
2820 */
2821 DEFINE_METHOD(bool, log_builtins_imp::item_set_int,
2822               (log_item_data * lid, longlong i)) {
2823   return log_item_set_int(lid, i);
2824 }
2825 
2826 /**
2827   Set a floating point value on a log_item.
2828   Fails gracefully if no log_item_data is supplied, so it can safely
2829   wrap log_line_item_set[_with_key]().
2830 
2831   @param  lid    log_item_data struct to set the value on
2832   @param  f      float to set
2833 
2834   @retval true   lid was nullptr (possibly: OOM, could not set up log_item)
2835   @retval false  all's well
2836 */
2837 DEFINE_METHOD(bool, log_builtins_imp::item_set_float,
2838               (log_item_data * lid, double f)) {
2839   return log_item_set_float(lid, f);
2840 }
2841 
2842 /**
2843   Set a string value on a log_item.
2844   Fails gracefully if no log_item_data is supplied, so it can safely
2845   wrap log_line_item_set[_with_key]().
2846 
2847   @param  lid    log_item_data struct to set the value on
2848   @param  s      pointer to string
2849   @param  s_len  length of string
2850 
2851   @retval true   lid was nullptr (possibly: OOM, could not set up log_item)
2852   @retval false  all's well
2853 */
2854 DEFINE_METHOD(bool, log_builtins_imp::item_set_lexstring,
2855               (log_item_data * lid, const char *s, size_t s_len)) {
2856   return log_item_set_lexstring(lid, s, s_len);
2857 }
2858 
2859 /**
2860   Set a string value on a log_item.
2861   Fails gracefully if no log_item_data is supplied, so it can safely
2862   wrap log_line_item_set[_with_key]().
2863 
2864   @param  lid    log_item_data struct to set the value on
2865   @param  s      pointer to NTBS
2866 
2867   @retval true   lid was nullptr (possibly: OOM, could not set up log_item)
2868   @retval false  all's well
2869 */
2870 DEFINE_METHOD(bool, log_builtins_imp::item_set_cstring,
2871               (log_item_data * lid, const char *s)) {
2872   return log_item_set_cstring(lid, s);
2873 }
2874 
2875 /**
2876   Create new log item with key name "key", and allocation flags of
2877   "alloc" (see enum_log_item_free).
2878   Will return a pointer to the item's log_item_data struct for
2879   convenience.
2880   This is mostly interesting for filters and other services that create
2881   items that are not part of a log_line; sources etc. that intend to
2882   create an item for a log_line (the more common case) should usually
2883   use the below line_item_set_with_key() which creates an item (like
2884   this function does), but also correctly inserts it into a log_line.
2885 
2886   @param  li     the log_item to work on
2887   @param  t      the item-type
2888   @param  key    the key to set on the item.
2889                  ignored for non-generic types (may pass nullptr for those)
2890                  see alloc
2891   @param  alloc  LOG_ITEM_FREE_KEY  if key was allocated by caller
2892                  LOG_ITEM_FREE_NONE if key was not allocated
2893                  Allocated keys will automatically free()d when the
2894                  log_item is.
2895                  The log_item's alloc flags will be set to the
2896                  submitted value; specifically, any pre-existing
2897                  value will be clobbered.  It is therefore WRONG
2898                  a) to use this on a log_item that already has a key;
2899                     it should only be used on freshly init'd log_items;
2900                  b) to use this on a log_item that already has a
2901                     value (specifically, an allocated one); the correct
2902                     order is to init a log_item, then set up type and
2903                     key, and finally to set the value. If said value is
2904                     an allocated string, the log_item's alloc should be
2905                     bitwise or'd with LOG_ITEM_FREE_VALUE.
2906 
2907   @retval        a pointer to the log_item's log_data, for easy chaining:
2908                  log_item_set_with_key(...)->data_integer= 1;
2909 */
2910 DEFINE_METHOD(log_item_data *, log_builtins_imp::item_set_with_key,
2911               (log_item * li, log_item_type t, const char *key, uint32 alloc)) {
2912   return log_item_set_with_key(li, t, key, alloc);
2913 }
2914 
2915 /**
2916   As log_item_set_with_key(), except that the key is automatically
2917   derived from the wellknown log_item_type t.
2918 
2919   Create new log item with type "t".
2920   Will return a pointer to the item's log_item_data struct for
2921   convenience.
2922   This is mostly interesting for filters and other services that create
2923   items that are not part of a log_line; sources etc. that intend to
2924   create an item for a log_line (the more common case) should usually
2925   use the below line_item_set_with_key() which creates an item (like
2926   this function does), but also correctly inserts it into a log_line.
2927 
2928   The allocation of this item will be LOG_ITEM_FREE_NONE;
2929   specifically, any pre-existing value will be clobbered.
2930   It is therefore WRONG
2931   a) to use this on a log_item that already has a key;
2932      it should only be used on freshly init'd log_items;
2933   b) to use this on a log_item that already has a
2934      value (specifically, an allocated one); the correct
2935      order is to init a log_item, then set up type and
2936      key, and finally to set the value. If said value is
2937      an allocated string, the log_item's alloc should be
2938      bitwise or'd with LOG_ITEM_FREE_VALUE.
2939 
2940   @param  li     the log_item to work on
2941   @param  t      the item-type
2942 
2943   @retval        a pointer to the log_item's log_data, for easy chaining:
2944                  log_item_set_with_key(...)->data_integer= 1;
2945 */
2946 DEFINE_METHOD(log_item_data *, log_builtins_imp::item_set,
2947               (log_item * li, log_item_type t)) {
2948   return log_item_set(li, t);
2949 }
2950 
2951 /**
2952   Create new log item in log line "ll", with key name "key", and
2953   allocation flags of "alloc" (see enum_log_item_free).
2954   On success, the number of registered items on the log line is increased,
2955   the item's type is added to the log_line's "seen" property,
2956   and a pointer to the item's log_item_data struct is returned for
2957   convenience.
2958 
2959   @param  ll        the log_line to work on
2960   @param  t         the item-type
2961   @param  key       the key to set on the item.
2962                     ignored for non-generic types (may pass nullptr for those)
2963                     see alloc
2964   @param  alloc     LOG_ITEM_FREE_KEY  if key was allocated by caller
2965                     LOG_ITEM_FREE_NONE if key was not allocated
2966                     Allocated keys will automatically free()d when the
2967                     log_item is.
2968                     The log_item's alloc flags will be set to the
2969                     submitted value; specifically, any pre-existing
2970                     value will be clobbered.  It is therefore WRONG
2971                     a) to use this on a log_item that already has a key;
2972                        it should only be used on freshly init'd log_items;
2973                     b) to use this on a log_item that already has a
2974                        value (specifically, an allocated one); the correct
2975                        order is to init a log_item, then set up type and
2976                        key, and finally to set the value. If said value is
2977                        an allocated string, the log_item's alloc should be
2978                        bitwise or'd with LOG_ITEM_FREE_VALUE.
2979 
2980 
2981   @retval !nullptr  a pointer to the log_item's log_data, for easy chaining:
2982                     line_item_set_with_key(...)->data_integer= 1;
2983   @retval  nullptr  could not create a log_item in given log_line
2984 */
2985 DEFINE_METHOD(log_item_data *, log_builtins_imp::line_item_set_with_key,
2986               (log_line * ll, log_item_type t, const char *key, uint32 alloc)) {
2987   return log_line_item_set_with_key(ll, t, key, alloc);
2988 }
2989 
2990 /**
2991   Create a new log item of well-known type "t" in log line "ll".
2992   On success, the number of registered items on the log line is increased,
2993   the item's type is added to the log_line's "seen" property,
2994   and a pointer to the item's log_item_data struct is returned for
2995   convenience.
2996 
2997   The allocation of this item will be LOG_ITEM_FREE_NONE;
2998   specifically, any pre-existing value will be clobbered.
2999   It is therefore WRONG
3000   a) to use this on a log_item that already has a key;
3001      it should only be used on freshly init'd log_items;
3002   b) to use this on a log_item that already has a
3003      value (specifically, an allocated one); the correct
3004      order is to init a log_item, then set up type and
3005      key, and finally to set the value. If said value is
3006      an allocated string, the log_item's alloc should be
3007      bitwise or'd with LOG_ITEM_FREE_VALUE.
3008 
3009   @param  ll        the log_line to work on
3010   @param  t         the item-type
3011 
3012   @retval !nullptr  a pointer to the log_item's log_data, for easy chaining:
3013                     line_item_set(...)->data_integer= 1;
3014   @retval  nullptr  could not create a log_item in given log_line
3015 */
3016 DEFINE_METHOD(log_item_data *, log_builtins_imp::line_item_set,
3017               (log_line * ll, log_item_type t)) {
3018   return log_line_item_set_with_key(ll, t, nullptr, LOG_ITEM_FREE_NONE);
3019 }
3020 
3021 /**
3022   Dynamically allocate and initialize a log_line.
3023 
3024   @retval nullptr  could not set up buffer (too small?)
3025   @retval other    address of the newly initialized log_line
3026 */
3027 DEFINE_METHOD(log_line *, log_builtins_imp::line_init, ()) {
3028   return log_line_init();
3029 }
3030 
3031 /**
3032   Release a log_line allocated with line_init()
3033 
3034   @param  ll       a log_line previously allocated with line_init()
3035 */
3036 DEFINE_METHOD(void, log_builtins_imp::line_exit, (log_line * ll)) {
3037   log_line_exit(ll);
3038 }
3039 
3040 /**
3041   How many items are currently set on the given log_line?
3042 
3043   @param   ll     the log-line to examine
3044 
3045   @retval         the number of items set
3046 */
3047 DEFINE_METHOD(int, log_builtins_imp::line_item_count, (log_line * ll)) {
3048   return log_line_item_count(ll);
3049 }
3050 
3051 /**
3052   Test whether a given type is presumed present on the log line.
3053 
3054   @param  ll  the log_line to examine
3055   @param  m   the log_type to test for
3056 
3057   @retval  0  not present
3058   @retval !=0 present
3059 */
3060 DEFINE_METHOD(log_item_type_mask, log_builtins_imp::line_item_types_seen,
3061               (log_line * ll, log_item_type_mask m)) {
3062   return log_line_item_types_seen(ll, m);
3063 }
3064 
3065 /**
3066   Get an iterator for the items in a log_line.
3067   For now, only one iterator may exist per log_line.
3068 
3069   @param  ll  the log_line to examine
3070 
3071   @retval     a log_iterm_iter, or nullptr on failure
3072 */
3073 DEFINE_METHOD(log_item_iter *, log_builtins_imp::line_item_iter_acquire,
3074               (log_line * ll)) {
3075   if (ll == nullptr) return nullptr;
3076 
3077   // If the default iter has already been claimed, refuse to overwrite it.
3078   if (ll->iter.ll != nullptr) return nullptr;
3079 
3080   ll->iter.ll = ll;
3081   ll->iter.index = -1;
3082 
3083   return &ll->iter;
3084 }
3085 
3086 /**
3087   Release an iterator for the items in a log_line.
3088 
3089   @param  it  the iterator to release
3090 */
3091 DEFINE_METHOD(void, log_builtins_imp::line_item_iter_release,
3092               (log_item_iter * it)) {
3093   DBUG_ASSERT(it != nullptr);
3094   DBUG_ASSERT(it->ll != nullptr);
3095 
3096   it->ll = nullptr;
3097 }
3098 
3099 /**
3100   Use the log_line iterator to get the first item from the set.
3101 
3102   @param  it  the iterator to use
3103 
3104   @retval  pointer to the first log_item in the collection, or nullptr
3105 */
3106 DEFINE_METHOD(log_item *, log_builtins_imp::line_item_iter_first,
3107               (log_item_iter * it)) {
3108   DBUG_ASSERT(it != nullptr);
3109   DBUG_ASSERT(it->ll != nullptr);
3110 
3111   if (it->ll->count < 1) return nullptr;
3112 
3113   it->index = 0;
3114   return &it->ll->item[it->index];
3115 }
3116 
3117 /**
3118   Use the log_line iterator to get the next item from the set.
3119 
3120   @param  it  the iterator to use
3121 
3122   @retval  pointer to the next log_item in the collection, or nullptr
3123 */
3124 // Call to function through pointer to incorrect function type
3125 DEFINE_METHOD(log_item *, log_builtins_imp::line_item_iter_next,
3126               (log_item_iter * it)) {
3127   DBUG_ASSERT(it != nullptr);
3128   DBUG_ASSERT(it->ll != nullptr);
3129   DBUG_ASSERT(it->index >= 0);
3130 
3131   it->index++;
3132 
3133   if (it->index >= it->ll->count) return nullptr;
3134 
3135   return &it->ll->item[it->index];
3136 }
3137 
3138 /**
3139   Use the log_line iterator to get the current item from the set.
3140 
3141   @param  it  the iterator to use
3142 
3143   @retval  pointer to the current log_item in the collection, or nullptr
3144 */
3145 DEFINE_METHOD(log_item *, log_builtins_imp::line_item_iter_current,
3146               (log_item_iter * it)) {
3147   DBUG_ASSERT(it != nullptr);
3148   DBUG_ASSERT(it->ll != nullptr);
3149   DBUG_ASSERT(it->index >= 0);
3150 
3151   if (it->index >= it->ll->count) return nullptr;
3152 
3153   return &it->ll->item[it->index];
3154 }
3155 
3156 /**
3157   Complete, filter, and write submitted log items.
3158 
3159   This expects a log_line collection of log-related key/value pairs,
3160   e.g. from log_message().
3161 
3162   Where missing, timestamp, priority, thread-ID (if any) and so forth
3163   are added.
3164 
3165   Log item source services, log item filters, and log item sinks are
3166   then called; then all applicable resources are freed.
3167 
3168   This interface is intended to facilitate the building of submission
3169   interfaces other than the variadic message() one below.  See the
3170   example fluent C++ LogEvent() wrapper for an example of how to leverage
3171   it.
3172 
3173   @param           ll                   key/value pairs describing info to log
3174 
3175   @retval          int                  number of fields in created log line
3176 */
3177 DEFINE_METHOD(int, log_builtins_imp::line_submit, (log_line * ll)) {
3178   return log_line_submit(ll);
3179 }
3180 
3181 /**
3182   Submit a log-message for log "log_type".
3183   Variadic convenience function for logging.
3184 
3185   This fills in the array that is used by the filter and log-writer
3186   services. Where missing, timestamp, priority, and thread-ID (if any)
3187   are added. Log item source services, log item filters, and log item
3188   writers are called.
3189 
3190   The variadic list accepts a list of "assignments" of the form
3191   - log_item_type, value,         for well-known types, and
3192   - log_item_type, key, value,    for ad-hoc types (LOG_ITEM_GEN_*)
3193 
3194   As its last item, the list should have
3195   - an element of type LOG_ITEM_LOG_MESSAGE, containing a printf-style
3196     format string, followed by all variables necessary to satisfy the
3197     substitutions in that string
3198 
3199     OR
3200 
3201   - an element of type LOG_ITEM_LOG_LOOKUP, containing a MySQL error code,
3202     which will be looked up in the list or regular error messages, followed
3203     by all variables necessary to satisfy the substitutions in that string
3204 
3205     OR
3206 
3207   - an element of type LOG_ITEM_LOG_VERBATIM, containing a string that will
3208     be used directly, with no % substitutions
3209 
3210   see log_vmessage() for more information.
3211 
3212   @param           log_type             what log should this go to?
3213   @param           ...                  fields: LOG_ITEM_* tag, [[key], value]
3214   @retval          int                  return value of log_vmessage()
3215 */
3216 DEFINE_METHOD(int, log_builtins_imp::message, (int log_type, ...)) {
3217   va_list lili;
3218   int ret;
3219 
3220   va_start(lili, log_type);
3221   ret = log_vmessage(log_type, lili);
3222   va_end(lili);
3223 
3224   return ret;
3225 }
3226 
3227 /*
3228   Escape \0 bytes, add \0 terminator. For log-sinks that terminate in
3229   an API using C-strings.
3230 
3231   @param  li  list_item to process
3232 
3233   @retval  -1 out of memory
3234   @retval  0  success
3235 */
3236 DEFINE_METHOD(int, log_builtins_imp::sanitize, (log_item * li)) {
3237   size_t in_len = li->data.data_string.length, out_len, len;
3238   const char *in_start = li->data.data_string.str, *in_read;
3239   char *out_start = nullptr, *out_write;
3240   int nuls_found = 0;
3241 
3242   DBUG_ASSERT((li != nullptr) && (li->item_class == LOG_LEX_STRING));
3243 
3244   // find out how many \0 to escape
3245   for (in_read = in_start, len = in_len;
3246        ((in_read = (const char *)memchr(in_read, '\0', len)) != nullptr);) {
3247     nuls_found++;
3248     in_read++;  // skip over \0
3249     len = in_len - (in_read - in_start);
3250   }
3251 
3252   /*
3253     Current length + 3 extra for each NUL so we can escape it + terminating NUL
3254   */
3255   out_len = in_len + (nuls_found * 3) + 1;
3256 
3257   if ((out_start = (char *)my_malloc(key_memory_log_error_loaded_services,
3258                                      out_len, MYF(0))) == nullptr)
3259     return -1;
3260 
3261   /*
3262     copy over
3263   */
3264   in_read = in_start;
3265   out_write = out_start;
3266 
3267   while (nuls_found--) {
3268     // copy part before NUL
3269     len = strlen(in_read);
3270     strcpy(out_write, in_read);
3271     out_write += len;
3272 
3273     // add escaped NUL
3274     strcpy(out_write, "\\000");
3275     out_write += 4;
3276     in_read += (len + 1);
3277   }
3278 
3279   // calculate tail (with no further NUL bytes) length
3280   len = (in_read > in_start) ? (in_read - in_start) : in_len;
3281 
3282   // copy tail
3283   strncpy(out_write, in_read, len);
3284 
3285   /*
3286     NUL terminate. (the formula above always gives a minimum out-size of 1.)
3287   */
3288   out_start[out_len - 1] = '\0';
3289 
3290   if (li->alloc & LOG_ITEM_FREE_VALUE) {
3291     my_free(const_cast<char *>(in_start));
3292   }
3293 
3294   li->data.data_string.str = out_start;
3295   li->alloc |= LOG_ITEM_FREE_VALUE;
3296 
3297   return 0;
3298 }
3299 
3300 /**
3301   Return MySQL error message for a given error code.
3302 
3303   @param  mysql_errcode  the error code the message for which to look up
3304 
3305   @retval                the message (a printf-style format string)
3306 */
3307 DEFINE_METHOD(const char *, log_builtins_imp::errmsg_by_errcode,
3308               (int mysql_errcode)) {
3309   return error_message_for_error_log(mysql_errcode);
3310 }
3311 
3312 /**
3313   Return MySQL error code for a given error symbol.
3314 
3315   @param  sym  the symbol to look up
3316 
3317   @retval  -1  failure
3318   @retval >=0  the MySQL error code
3319 */
3320 DEFINE_METHOD(longlong, log_builtins_imp::errcode_by_errsymbol,
3321               (const char *sym)) {
3322   return mysql_symbol_to_errno(sym);
3323 }
3324 
3325 /**
3326   Convenience function: Derive a log label ("error", "warning",
3327   "information") from a severity.
3328 
3329   @param   prio       the severity/prio in question
3330 
3331   @return             a label corresponding to that priority.
3332   @retval  "Error"    for prio of ERROR_LEVEL or higher
3333   @retval  "Warning"  for prio of WARNING_LEVEL
3334   @retval  "Note"     otherwise
3335 */
3336 DEFINE_METHOD(const char *, log_builtins_imp::label_from_prio, (int prio)) {
3337   return log_label_from_prio(prio);
3338 }
3339 
3340 /**
3341   open an error log file
3342 
3343   @param       file          if beginning with '.':
3344                                @@global.log_error, except with this extension
3345                              otherwise:
3346                                use this as file name in the same location as
3347                                @@global.log_error
3348 
3349                              Value may not contain folder separators!
3350 
3351   @param[out]  my_errstream  an error log handle, or nullptr on failure
3352 
3353   @retval      0             success
3354   @retval     -1             EINVAL: my_errstream is NULL
3355   @retval     -2             EINVAL: invalid file name / extension
3356   @retval     -3             OOM: could not allocate file handle
3357   @retval     -4             couldn't lock lock
3358   @retval     -5             couldn't write to given location
3359 */
3360 DEFINE_METHOD(int, log_builtins_imp::open_errstream,
3361               (const char *file, void **my_errstream)) {
3362   log_errstream *les;
3363   int rr;
3364 
3365   if (my_errstream == nullptr) return -1;
3366 
3367   *my_errstream = nullptr;
3368 
3369   les = (log_errstream *)my_malloc(key_memory_log_error_loaded_services,
3370                                    sizeof(log_errstream), MYF(0));
3371 
3372   if (les == nullptr) return -3;
3373 
3374   new (les) log_errstream();
3375 
3376   if (mysql_mutex_init(0, &les->LOCK_errstream, MY_MUTEX_INIT_FAST)) {
3377     my_free(les);
3378     return -4;
3379   }
3380 
3381   if ((file == nullptr) || (file[0] == '\0') ||
3382       (strchr(file, FN_LIBCHAR) != nullptr)) {
3383     rr = -2;
3384     goto fail_with_free;
3385   } else if ((log_error_dest == nullptr) ||
3386              (!strcmp(log_error_dest, "stderr"))) {
3387     // using default stream, no file struct needed
3388     les->file = nullptr;
3389   } else {
3390     char errorlog_filename_buff[FN_REFLEN];
3391     char path[FN_REFLEN];
3392     size_t path_length;
3393     MY_STAT f_stat;
3394 
3395     dirname_part(path, log_error_dest, &path_length);
3396 
3397     rr = -5;
3398 
3399     if (file[0] == '.') {
3400       fn_format(errorlog_filename_buff, log_error_dest, path, file,
3401                 MY_APPEND_EXT | MY_REPLACE_DIR);
3402 
3403     } else {
3404       fn_format(errorlog_filename_buff, file, path, ".les", MY_REPLACE_DIR);
3405     }
3406 
3407     if (my_stat(errorlog_filename_buff, &f_stat, MYF(0)) != nullptr) {
3408       if (!(f_stat.st_mode & MY_S_IWRITE)) goto fail_with_free;
3409     } else {
3410       if (path_length && my_access(path, (F_OK | W_OK))) goto fail_with_free;
3411     }
3412 
3413     les->file = my_fopen(errorlog_filename_buff,
3414                          O_APPEND | O_WRONLY | MY_FOPEN_BINARY, MYF(0));
3415 
3416     if (les->file == nullptr) goto fail_with_free;
3417   }
3418 
3419   *my_errstream = les;
3420 
3421   return 0;
3422 
3423 fail_with_free:
3424   my_free(les);
3425 
3426   return rr;
3427 }
3428 
3429 /**
3430   write to an error log file previously opened with open_errstream()
3431 
3432   @param       my_errstream  a handle describing the log file
3433   @param       buffer        pointer to the string to write
3434   @param       length        length of the string to write
3435 
3436   @retval  0                 success
3437   @retval !0                 failure
3438 */
3439 DEFINE_METHOD(int, log_builtins_imp::write_errstream,
3440               (void *my_errstream, const char *buffer, size_t length)) {
3441   log_errstream *les = (log_errstream *)my_errstream;
3442 
3443   if ((les == nullptr) || (les->file == nullptr))
3444     log_write_errstream(buffer, length);
3445   else {
3446     mysql_mutex_lock(&les->LOCK_errstream);
3447     fprintf(les->file, "%.*s\n", (int)length, buffer);
3448     fflush(les->file);
3449     mysql_mutex_unlock(&les->LOCK_errstream);
3450   }
3451 
3452   return 0;
3453 }
3454 
3455 /**
3456   are we writing to a dedicated errstream, or are we sharing it?
3457 
3458   @param       my_errstream  a handle describing the log file
3459 
3460   @retval <0                 error
3461   @retval  0                 not dedicated (multiplexed, stderr, ...)
3462   @retval  1                 dedicated
3463 */
3464 DEFINE_METHOD(int, log_builtins_imp::dedicated_errstream,
3465               (void *my_errstream)) {
3466   log_errstream *les = (log_errstream *)my_errstream;
3467 
3468   if (les == nullptr) return -1;
3469 
3470   return (les->file != nullptr) ? 1 : 0;
3471 }
3472 
3473 /**
3474   close an error log file previously opened with open_errstream()
3475 
3476   @param       my_errstream  a handle describing the log file
3477 
3478   @retval      0          success
3479   @retval     !0          failure
3480 */
3481 DEFINE_METHOD(int, log_builtins_imp::close_errstream, (void **my_errstream)) {
3482   int rr;
3483 
3484   if (my_errstream == nullptr) return -1;
3485 
3486   log_errstream *les = (log_errstream *)(*my_errstream);
3487 
3488   if (les == nullptr) return -2;
3489 
3490   *my_errstream = nullptr;
3491 
3492   if (les->file != nullptr) {
3493     my_fclose(les->file, MYF(0));
3494     // Continue to log after closing, you'll log to stderr. That'll learn ya.
3495     les->file = nullptr;
3496   }
3497 
3498   rr = mysql_mutex_destroy(&les->LOCK_errstream);
3499 
3500   my_free(les);
3501 
3502   return rr;
3503 }
3504 
3505 /*
3506   Service: some stand-ins for string functions we need until they are
3507   implemented in a more comprehensive service.
3508   3rd party services should not rely on these being here forever.
3509 */
3510 
3511 /**
3512   Wrapper for my_malloc()
3513 
3514   Alloc (len+1) bytes.
3515 
3516   @param len  length of string to copy
3517 */
3518 DEFINE_METHOD(void *, log_builtins_string_imp::malloc, (size_t len)) {
3519   return my_malloc(key_memory_log_error_loaded_services, len, MYF(0));
3520 }
3521 
3522 /**
3523   Wrapper for my_strndup()
3524 
3525   Alloc (len+1) bytes, then copy len bytes from fm, and \0 terminate.
3526   Like my_strndup(), and unlike strndup(), \0 in input won't end copying.
3527 
3528   @param fm   string to copy
3529   @param len  length of string to copy
3530 */
3531 DEFINE_METHOD(char *, log_builtins_string_imp::strndup,
3532               (const char *fm, size_t len)) {
3533   return my_strndup(key_memory_log_error_loaded_services, fm, len, MYF(0));
3534 }
3535 
3536 /**
3537   Wrapper for my_free() - free allocated memory
3538 */
3539 DEFINE_METHOD(void, log_builtins_string_imp::free, (void *ptr)) {
3540   return my_free(ptr);
3541 }
3542 
3543 /**
3544   Wrapper for strlen() - length of a nul-terminated byte string
3545 */
3546 DEFINE_METHOD(size_t, log_builtins_string_imp::length, (const char *s)) {
3547   return strlen(s);
3548 }
3549 
3550 /**
3551   Wrapper for strchr() - find character in string, from the left
3552 */
3553 DEFINE_METHOD(char *, log_builtins_string_imp::find_first,
3554               (const char *s, int c)) {
3555   return strchr(const_cast<char *>(s), c);
3556 }
3557 
3558 /**
3559   Wrapper for strrchr() - find character in string, from the right
3560 */
3561 DEFINE_METHOD(char *, log_builtins_string_imp::find_last,
3562               (const char *s, int c)) {
3563   return strrchr(const_cast<char *>(s), c);
3564 }
3565 
3566 /**
3567   Compare two NUL-terminated byte strings
3568 
3569   Note that when comparing without length limit, the long string
3570   is greater if they're equal up to the length of the shorter
3571   string, but the shorter string will be considered greater if
3572   its "value" up to that point is greater:
3573 
3574   compare 'abc','abcd':      -100  (longer wins if otherwise same)
3575   compare 'abca','abcd':       -3  (higher value wins)
3576   compare 'abcaaaaa','abcd':   -3  (higher value wins)
3577 
3578   @param  a                 the first string
3579   @param  b                 the second string
3580   @param  len               compare at most this many characters --
3581                             0 for no limit
3582   @param  case_insensitive  ignore upper/lower case in comparison
3583 
3584   @retval -1                a < b
3585   @retval  0                a == b
3586   @retval  1                a > b
3587 */
3588 DEFINE_METHOD(int, log_builtins_string_imp::compare,
3589               (const char *a, const char *b, size_t len,
3590                bool case_insensitive)) {
3591   return log_string_compare(a, b, len, case_insensitive);
3592 }
3593 
3594 /**
3595   Wrapper for vsnprintf()
3596   Replace all % in format string with variables from list
3597 
3598   @param  to    buffer to write the result to
3599   @param  n     size of that buffer
3600   @param  fmt   format string
3601   @param  ap    va_list with valuables for all substitutions in format string
3602 
3603   @retval       return value of vsnprintf
3604 */
3605 DEFINE_METHOD(size_t, log_builtins_string_imp::substitutev,
3606               (char *to, size_t n, const char *fmt, va_list ap)) {
3607   return vsnprintf(to, n, fmt, ap);
3608 }
3609 
3610 /**
3611   Wrapper for vsnprintf()
3612   Replace all % in format string with variables from list
3613 */
3614 DEFINE_METHOD(size_t, log_builtins_string_imp::substitute,
3615               (char *to, size_t n, const char *fmt, ...)) {
3616   size_t ret;
3617   va_list ap;
3618 
3619   va_start(ap, fmt);
3620   ret = vsnprintf(to, n, fmt, ap);
3621   va_end(ap);
3622   return ret;
3623 }
3624 
3625 /*
3626   Service: some stand-ins we need until certain other WLs are implemented.
3627   3rd party services should not rely on these being here for long.
3628 */
3629 
3630 DEFINE_METHOD(size_t, log_builtins_tmp_imp::notify_client,
3631               (void *thd, uint severity, uint code, char *to, size_t n,
3632                const char *format, ...)) {
3633   size_t ret = 0;
3634 
3635   if ((to != nullptr) && (n > 0)) {
3636     va_list ap;
3637 
3638     va_start(ap, format);
3639     ret = vsnprintf(to, n, format, ap);
3640     va_end(ap);
3641 
3642     push_warning((THD *)thd, (Sql_condition::enum_severity_level)severity, code,
3643                  to);
3644   }
3645 
3646   return ret;
3647 }
3648 
3649 /*
3650   Service: expose syslog/eventlog to other components.
3651   3rd party services should not rely on these being here for long,
3652   as this may be merged into a possibly mysys API later.
3653 */
3654 
3655 /**
3656   Wrapper for mysys' my_openlog.
3657   Opens/Registers a new handle for system logging.
3658   Note: It's a thread-unsafe function. It should either
3659   be invoked from the main thread or some extra thread
3660   safety measures need to be taken.
3661 
3662   @param name     Name of the event source / syslog ident.
3663   @param option   MY_SYSLOG_PIDS to log PID with each message.
3664   @param facility Type of program. Passed to openlog().
3665 
3666   @retval   0 Success
3667   @retval  -1 Error, log not opened
3668   @retval  -2 Error, not updated, using previous values
3669 */
3670 DEFINE_METHOD(int, log_builtins_syseventlog_imp::open,
3671               (const char *name, int option, int facility)) {
3672   int ret;
3673 
3674   mysql_mutex_lock(&THR_LOCK_log_syseventlog);
3675   ret = my_openlog(name, option, facility);
3676   mysql_mutex_unlock(&THR_LOCK_log_syseventlog);
3677 
3678   return ret;
3679 }
3680 
3681 /**
3682   Wrapper for mysys' my_syslog.
3683   Sends message to the system logger. On Windows, the specified message is
3684   internally converted to UCS-2 encoding, while on other platforms, no
3685   conversion takes place and the string is passed to the syslog API as it is.
3686 
3687   @param level                Log level
3688   @param msg                  Message to be logged
3689 
3690   @retval   0  Success
3691   @retval <>0  Error
3692 */
3693 DEFINE_METHOD(int, log_builtins_syseventlog_imp::write,
3694               (enum loglevel level, const char *msg)) {
3695   int ret;
3696 
3697   mysql_mutex_lock(&THR_LOCK_log_syseventlog);
3698   ret = my_syslog(&my_charset_utf8_bin, level, msg);
3699   mysql_mutex_unlock(&THR_LOCK_log_syseventlog);
3700 
3701   return ret;
3702 }
3703 
3704 /**
3705   Wrapper for mysys' my_closelog.
3706   Closes/de-registers the system logging handle.
3707 
3708   @retval   0 Success
3709   @retval <>0 Error
3710 */
3711 DEFINE_METHOD(int, log_builtins_syseventlog_imp::close, (void)) {
3712   int ret = 0;
3713 
3714   mysql_mutex_lock(&THR_LOCK_log_syseventlog);
3715   ret = my_closelog();
3716   mysql_mutex_unlock(&THR_LOCK_log_syseventlog);
3717 
3718   return ret;
3719 }
3720