CUBRID Engine  latest
error_manager.c
Go to the documentation of this file.
1 /*
2  * Copyright 2008 Search Solution Corporation
3  * Copyright 2016 CUBRID Corporation
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 /*
20  * error_manager.c - Error module (both client & server)
21  */
22 
23 #ident "$Id$"
24 
25 // fix porting issue _lseek64
26 #if defined (WINDOWS)
27 #include <io.h>
28 #endif /* WINDOWS */
29 
30 #include "error_manager.h"
31 
32 #include "chartype.h"
33 #include "error_context.hpp"
34 #include "environment_variable.h"
35 #if defined (SERVER_MODE)
36 #include "log_impl.h"
37 #endif /* !SERVER_MODE */
38 #include "memory_alloc.h"
39 #include "message_catalog.h"
40 #include "object_representation.h"
41 #if !defined (WINDOWS)
42 #include "release_string.h"
43 #endif // not WINDOWS
44 #include "system_parameter.h"
45 #include "stack_dump.h"
46 #if !defined (SERVER_MODE)
47 #include "transaction_cl.h"
48 #endif /* !SERVER_MODE */
49 
50 #if !defined (WINDOWS) && defined (SERVER_MODE)
51 #include "boot_sr.h"
52 #endif /* !WINDWS && SERVER_MODE */
53 
54 // c++ headers
55 #include <cassert>
56 #include <cstddef>
57 #include <cstring>
58 
59 // c headers
60 #include <errno.h>
61 #if defined (SOLARIS)
62 #include <netdb.h>
63 #endif /* SOLARIS */
64 #if defined (WINDOWS)
65 #include <process.h>
66 #endif // WINDOWS
67 #if defined (SERVER_MODE) && !defined (WINDOWS)
68 #include <pthread.h>
69 #endif /* SERVER_MODE && !WINDOWS */
70 #include <setjmp.h>
71 #include <signal.h>
72 #include <stdarg.h>
73 #include <stdio.h>
74 #include <stdlib.h>
75 #include <string.h>
76 #if !defined(CS_MODE)
77 #include <sys/stat.h>
78 #endif /* !CS_MODE */
79 #if !defined (WINDOWS)
80 #include <sys/time.h>
81 #endif /* !WINDOWS */
82 #if !defined(CS_MODE)
83 #include <sys/types.h>
84 #endif /* !CS_MODE */
85 #if !defined (WINDOWS)
86 #include <syslog.h>
87 #endif /* !WINDOWS */
88 #include <time.h>
89 #if !defined (WINDOWS)
90 #include <unistd.h>
91 #endif /* !WINDOWS */
92 
93 #include <mutex>
94 
95 /*
96  * Definition of error message structure. One structure is defined for each
97  * thread of execution. Note message areas are stored in the structure for
98  * multi-threading purposes.
99  */
100 typedef struct er_copy_area ER_COPY_AREA;
102 {
103  int err_id; /* error identifier of the current message */
104  int severity; /* warning, error, FATAL error, etc... */
105  int length_msg; /* length of the message */
106  char area[1]; /* actually, more than one */
107 };
108 
109 typedef struct er_spec ER_SPEC;
110 struct er_spec
111 {
112  int width; /* minimum width of field */
113  char code; /* what to retrieve from the va_list int, long, double, long double or char */
114  char spec[10]; /* buffer to hold the actual sprintf code */
115 };
116 
117 typedef struct er_fmt ER_FMT;
118 struct er_fmt
119 {
120  int err_id; /* The int associated with the msg */
121  char *fmt; /* A printf-style format for the msg */
122  ER_SPEC *spec; /* Pointer to real array; points to spec_buf if nspecs < DIM(spec_buf) */
123  int fmt_length; /* The strlen() of fmt */
124  int must_free; /* TRUE if fmt must be free_and_initd */
125  int nspecs; /* The number of format specs in fmt */
126  int spec_top; /* The capacity of spec */
127  ER_SPEC spec_buf[16]; /* Array of format specs for args */
128 };
129 
130 using namespace cuberr; // until we add everything under cuberr
131 
132 #if defined(WINDOWS)
133 #define LOG_ALERT 0
134 static int
135 syslog (long priority, const char *message, ...)
136 {
137  return 0;
138 }
139 #endif
140 
141 // *INDENT-OFF*
144 // *INDENT-ON*
145 
146 /*
147  * These are done via complied constants rather than the message catalog, because they must be available if the message
148  * catalog is not available.
149  */
150 static const char *er_severity_string[] = { "FATAL ERROR", "ERROR", "SYNTAX ERROR", "WARNING", "NOTIFICATION" };
151 
152 static const char *er_unknown_severity = "Unknown severity level";
153 
154 #define ER_SEVERITY_STRING(severity) \
155  (((severity) >= ER_FATAL_ERROR_SEVERITY && (severity) <= ER_MAX_SEVERITY) \
156  ? er_severity_string[severity] : er_unknown_severity)
157 
158 #define ER_ERROR_WARNING_STRING(severity) \
159  (((severity) >= ER_FATAL_ERROR_SEVERITY && (severity) < ER_WARNING_SEVERITY) \
160  ? er_severity_string[ER_ERROR_SEVERITY] : "")
161 
162 
163 /*
164  * Message sets within the er.msg catalog. Set #1 comprises the system
165  * error messages proper, while set #2 comprises the specific messages
166  * that the er_ module uses itself.
167  */
168 #define ER_MSG_SET 1
169 #define ER_INTERNAL_MSG_SET 2
170 
171 #define PRM_ER_MSGLEVEL 0
172 
173 #define ER_MALLOC(size) er_malloc_helper ((size), __FILE__, __LINE__)
174 
175 #define SPEC_CODE_LONGLONG ((char)0x88)
176 #define SPEC_CODE_SIZE_T ((char)0x89)
177 
178 #define MAX_LINE 4096
179 
180 /*
181  * Default text for messages. These messages are used only when we can't
182  * locate a message catalog from which to read their counterparts. There
183  * is no need to localize these messages.
184  *
185  * Make sure that this enumeration remains consistent with the following
186  * array of default messages and with the message entries in every
187  * message catalog. If you add a new code, you need to add a new entry
188  * to default_internal_msg[] and to every catalog.
189  */
191 {
192  /* skip msg no 0 */
217 };
218 
219 static const char *er_Builtin_msg[] = {
220  /* skip msg no 0 */
221  NULL,
222  /* ER_ER_HEADER */
223  "Error in error subsystem (line %d): ",
224  /*
225  * ER_ER_MISSING_MSG
226  *
227  * It's important that this message have no conversion specs, because
228  * it is sometimes used to replace messages in which we have no
229  * confidence (e.g., because they're proven not to have the same
230  * number of conversion specs as arguments they are given). In those
231  * cases we can't rely on the arguments at all, and we must use a
232  * format that won't try to do anything with va_arg().
233  */
234  "No error message available.",
235  /* ER_ER_OUT_OF_MEMORY */
236  "Can't allocate %d bytes.",
237  /* ER_ER_NO_CATALOG */
238  "Can't find message catalog files.",
239  /* ER_ER_LOG_MISSING_MSG */
240  "Missing message for error code %d.",
241  /* ER_ER_EXIT */
242  "\n... ABORT/EXIT IMMEDIATELY ...\n",
243  /* ER_ER_ASK */
244  "Do you want to exit? 1/0 ",
245  /* ER_ER_UNKNOWN_FILE */
246  "Unknown file",
247  /* ER_ER_SUBSTITUTE_MSG */
248  "No message available; original message format in error.",
249  /* ER_LOG_ASK_VALUE */
250  "er_init: *** Incorrect exit_ask value = %d; will assume %s instead. ***\n",
251  /* ER_LOG_MSGLOG_WARNING */
252  "er_init: *** WARNING: Unable to open message log \"%s\"; will assume stderr instead. ***\n",
253  /* ER_LOG_SUSPECT_FMT */
254  "er_study_fmt: suspect message for error code %d.",
255  /* ER_LOG_UNKNOWN_CODE */
256  "er_estimate_size: unknown conversion code (%d).",
257  /* ER_LOG_WRAPAROUND */
258  "\n\n*** Message log wraparound. Messages will continue at top of the file. ***\n\n",
259  /* ER_LOG_MSG_WRAPPER */
260  "\nTime: %s - %s *** %s CODE = %d, Tran = %d%s\n%s\n",
261  /* ER_LOG_SYSLOG_WRAPPER */
262  "CUBRID (pid: %d) *** %s *** %s CODE = %d, Tran = %d, %s",
263  /* ER_LOG_MSG_WRAPPER_D */
264  "\nTime: %s - %s *** file %s, line %d %s CODE = %d, Tran = %d%s\n%s\n",
265  /* ER_LOG_SYSLOG_WRAPPER_D */
266  "CUBRID (pid: %d) *** %s *** file %s, line %d, %s CODE = %d, Tran = %d. %s",
267  /* ER_LOG_LAST_MSG */
268  "\n*** The previous error message is the last one. ***\n\n",
269  /* ER_LOG_DEBUG_NOTIFY */
270  "\nTime: %s - DEBUG *** file %s, line %d\n",
271  /* ER_STOP_MAIL_SUBJECT */
272  "Mail -s \"CUBRID has been stopped\" ",
273  /* ER_STOP_MAIL_BODY */
274  "--->>>\n%s has been stopped at your request when the following error was set:\nerrid = %d, %s\nUser: %s, pid: %d, host: %s, time: %s<<<---",
275  /* ER_STOP_SYSLOG */
276  "%s has been stopped on errid = %d. User: %s, pid: %d",
277  /* ER_EVENT_HANDLER */
278  "er_init: cannot install event handler \"%s\""
279 };
280 
281 static char *er_Cached_msg[sizeof (er_Builtin_msg) / sizeof (const char *)];
282 static bool er_Is_cached_msg = false;
283 
284 /* Error log message file related */
285 #define ER_MSG_LOG_FILE_SUFFIX ".err"
286 static char er_Msglog_filename_buff[PATH_MAX];
287 static const char *er_Msglog_filename = NULL;
288 static FILE *er_Msglog_fh = NULL;
289 
290 /* Error log message file related */
291 #define ER_ACCESS_LOG_FILE_SUFFIX ".access"
292 static char er_Accesslog_filename_buff[PATH_MAX];
293 static const char *er_Accesslog_filename = NULL;
294 static FILE *er_Accesslog_fh = NULL;
295 
296 
300 #if !defined (SERVER_MODE)
302 #endif /* !SERVER_MODE */
303 static unsigned int er_Eid = 0;
304 
305 /* Event handler related */
306 static FILE *er_Event_pipe = NULL;
307 static bool er_Event_started = false;
308 static jmp_buf er_Event_jmp_buf;
310 
311 /* Other supporting global variables */
312 static bool er_Logfile_opened = false;
313 static bool er_Hasalready_initiated = false;
314 static bool er_Has_sticky_init = false;
315 static bool er_Isa_null_device = false;
318 /* TODO : remove this when applylogdb and copylogdb are removed
319  * multithreaded client processes which start+end database (and error module) in a loop, may need to log errors on
320  * other threads (while error module is stopped); this flag prevents assertion failure of error module initialization
321 * for such case */
322 #if defined (CS_MODE)
323 static bool er_Ignore_uninit = false;
324 #endif
325 
326 #if !defined (SERVER_MODE)
327 // requires own context
329 #endif // not SERVER_MODE
330 
331 static void er_event_sigpipe_handler (int sig);
332 static void er_event (void);
333 static int er_event_init (void);
334 static void er_event_final (void);
335 
336 #if defined (SERVER_MODE) || defined (SA_MODE)
337 static void er_set_access_log_filename (void);
338 #endif /* SERVER_MODE || SA_MODE */
339 
340 static FILE *er_file_open (const char *path);
341 static bool er_file_isa_null_device (const char *path);
342 static FILE *er_file_backup (FILE * fp, const char *path);
343 
344 static void er_call_stack_dump_on_error (int severity, int err_id);
345 static void er_notify_event_on_error (int err_id);
346 static int er_set_internal (int severity, const char *file_name, const int line_no, int err_id, int num_args,
347  bool include_os_error, FILE * fp, va_list * ap_ptr);
348 static void er_log (int err_id);
349 static void er_stop_on_error (void);
350 
351 static int er_study_spec (const char *conversion_spec, char *simple_spec, int *position, int *width, int *va_class);
352 static void er_study_fmt (ER_FMT * fmt);
353 static size_t er_estimate_size (ER_FMT * fmt, va_list * ap);
354 static ER_FMT *er_find_fmt (int err_id, int num_args);
355 static void er_init_fmt (ER_FMT * fmt);
356 static ER_FMT *er_create_fmt_msg (ER_FMT * fmt, int err_id, const char *msg);
357 static void er_clear_fmt (ER_FMT * fmt);
358 static void er_internal_msg (ER_FMT * fmt, int code, int msg_num);
359 static void *er_malloc_helper (std::size_t size, const char *file, int line);
360 static void er_emergency (const char *file, int line, const char *fmt, ...);
361 static int er_vsprintf (er_message * er_entry_p, ER_FMT * fmt, va_list * ap);
362 
363 static int er_call_stack_init (void);
364 static int er_fname_free (const void *key, void *data, void *args);
365 static void er_call_stack_final (void);
366 
367 static void _er_log_debug_internal (const char *file_name, const int line_no, const char *fmt, va_list * ap);
368 
370 
371 /* vector of functions to call when an error is set */
373  er_log, /* ER_FATAL_ERROR_SEVERITY */
374  er_log, /* ER_ERROR_SEVERITY */
375  er_log, /* ER_SYNTAX_ERROR_SEVERITY */
376  er_log, /* ER_WARNING_SEVERITY */
377  er_log /* ER_NOTIFICATION_SEVERITY */
378 };
379 
380 /*
381  * er_get_msglog_filename - Find the error message log file name
382  * return: log file name
383  */
384 const char *
386 {
387  return er_Msglog_filename;
388 }
389 
390 /*
391  * er_event_sigpipe_handler
392  */
393 static void
395 {
396  _longjmp (er_Event_jmp_buf, 1);
397 }
398 
399 /*
400  * er_event - Notify a error event to the handler
401  * return: none
402  */
403 static void
404 er_event (void)
405 {
406  int err_id, severity, nlevels, line_no;
407  const char *file_name, *msg;
408 
409  if (er_Event_pipe == NULL || er_Event_started == false)
410  {
411  return;
412  }
413 
414  /* Get the most detailed error message available */
415  er_all (&err_id, &severity, &nlevels, &line_no, &file_name, &msg);
416 
417 #if !defined(WINDOWS)
418  saved_Sig_handler = os_set_signal_handler (SIGPIPE, er_event_sigpipe_handler);
419 #endif /* not WINDOWS */
420  if (_setjmp (er_Event_jmp_buf) == 0)
421  {
422  fprintf (er_Event_pipe, "%d %s %s\n", err_id, ER_SEVERITY_STRING (severity), msg);
423  fflush (er_Event_pipe);
424  }
425  else
426  {
427  er_Event_started = false;
428  if (er_Hasalready_initiated)
429  {
431  }
432  if (er_Event_pipe != NULL)
433  {
434  fclose (er_Event_pipe);
435  er_Event_pipe = NULL;
436  }
437  }
438 #if !defined(WINDOWS)
439  os_set_signal_handler (SIGPIPE, saved_Sig_handler);
440 #endif /* not WINDOWS */
441 }
442 
443 /*
444  * er_evnet_init
445  */
446 static int
448 {
449  volatile int error = NO_ERROR;
450  const char *msg;
451 
452 #if !defined(WINDOWS)
453  saved_Sig_handler = os_set_signal_handler (SIGPIPE, er_event_sigpipe_handler);
454 #endif /* not WINDOWS */
455  if (_setjmp (er_Event_jmp_buf) == 0)
456  {
457  er_Event_started = false;
458  if (er_Event_pipe != NULL)
459  {
460  if (er_Hasalready_initiated)
461  {
463  }
465  fprintf (er_Event_pipe, "%d %s %s", ER_EV_STOPPED, ER_SEVERITY_STRING (ER_NOTIFICATION_SEVERITY),
466  (msg ? msg : "?"));
467 
468  fflush (er_Event_pipe);
469  pclose (er_Event_pipe);
470  er_Event_pipe = NULL;
471  }
472 
473  er_Event_pipe = popen (prm_get_string_value (PRM_ID_EVENT_HANDLER), "w");
474  if (er_Event_pipe != NULL)
475  {
476  if (er_Hasalready_initiated)
477  {
479  }
481  fprintf (er_Event_pipe, "%d %s %s", ER_EV_STARTED, ER_SEVERITY_STRING (ER_NOTIFICATION_SEVERITY),
482  (msg ? msg : "?"));
483 
484  fflush (er_Event_pipe);
485  er_Event_started = true;
486  }
487  else
488  {
489  if (er_Hasalready_initiated)
490  {
493  }
494  error = ER_EV_CONNECT_HANDLER;
495  }
496  }
497  else
498  {
499  if (er_Hasalready_initiated)
500  {
502  }
503  if (er_Event_pipe != NULL)
504  {
505  fclose (er_Event_pipe);
506  er_Event_pipe = NULL;
507  }
508  error = ER_EV_BROKEN_PIPE;
509  }
510 #if !defined(WINDOWS)
511  os_set_signal_handler (SIGPIPE, saved_Sig_handler);
512 #endif /* not WINDOWS */
513 
514  return error;
515 }
516 
517 /*
518  * er_event_final
519  */
520 static void
522 {
523  const char *msg;
524 
525 #if !defined(WINDOWS)
526  saved_Sig_handler = os_set_signal_handler (SIGPIPE, er_event_sigpipe_handler);
527 #endif /* not WINDOWS */
528  if (_setjmp (er_Event_jmp_buf) == 0)
529  {
530  er_Event_started = false;
531  if (er_Event_pipe != NULL)
532  {
533  if (er_Hasalready_initiated)
534  {
536  }
538  fprintf (er_Event_pipe, "%d %s %s", ER_EV_STOPPED, ER_SEVERITY_STRING (ER_NOTIFICATION_SEVERITY),
539  (msg ? msg : "?"));
540 
541  fflush (er_Event_pipe);
542  pclose (er_Event_pipe);
543  er_Event_pipe = NULL;
544  }
545  }
546  else
547  {
548  if (er_Hasalready_initiated)
549  {
551  }
552  if (er_Event_pipe != NULL)
553  {
554  fclose (er_Event_pipe);
555  er_Event_pipe = NULL;
556  }
557  }
558 #if !defined(WINDOWS)
559  os_set_signal_handler (SIGPIPE, saved_Sig_handler);
560 #endif /* not WINDOWS */
561 }
562 
563 /*
564  * er_call_stack_init -
565  * return: error code
566  */
567 static int
569 {
570 #if defined(LINUX)
571  fname_table = mht_create (0, 100, mht_5strhash, mht_compare_strings_are_equal);
572  if (fname_table == NULL)
573  {
574  return ER_FAILED;
575  }
576 #endif
577 
578  return NO_ERROR;
579 }
580 
581 /*
582  * er_call_stack_final -
583  * return: none
584  */
585 static void
587 {
588 #if defined(LINUX)
589  if (fname_table == NULL)
590  {
591  return;
592  }
593 
594  mht_map (fname_table, er_fname_free, NULL);
595  mht_destroy (fname_table);
596  fname_table = NULL;
597 #endif
598 }
599 
600 /*
601  * er_fname_free -
602  * return: error code
603  */
604 static int
605 er_fname_free (const void *key, void *data, void *args)
606 {
607  free ((void *) key);
608  free (data);
609 
610  return NO_ERROR;
611 }
612 
613 #if defined (SERVER_MODE) || defined (SA_MODE)
614 /*
615  * er_set_access_log_filename - set er_Accesslog_filename_buff, er_Accesslog_filename
616  * return:
617  */
618 static void
619 er_set_access_log_filename (void)
620 {
621  char tmp[PATH_MAX];
622  std::size_t len, suffix_len;
623 
624  if (er_Msglog_filename == NULL)
625  {
626  er_Accesslog_filename = NULL;
627  return;
628  }
629 
630  len = std::strlen (er_Msglog_filename);
631  suffix_len = std::strlen (ER_MSG_LOG_FILE_SUFFIX);
632 
633  if (len < suffix_len || strncmp (&er_Msglog_filename[len - suffix_len], ER_MSG_LOG_FILE_SUFFIX, suffix_len) != 0)
634  {
635  if (snprintf (er_Accesslog_filename_buff, PATH_MAX, "%s%s", er_Msglog_filename, ER_ACCESS_LOG_FILE_SUFFIX) < 0)
636  {
637  er_Accesslog_filename = NULL;
638  return;
639  }
640  /* ex) server.log => server.log.access */
641  }
642  else
643  {
644  strncpy (tmp, er_Msglog_filename, PATH_MAX);
645  tmp[len - suffix_len] = '\0';
646  if (snprintf (er_Accesslog_filename_buff, PATH_MAX - 1, "%s%s", tmp, ER_ACCESS_LOG_FILE_SUFFIX) < 0)
647  {
648  er_Accesslog_filename = NULL;
649  return;
650  }
651  /* ex) server_log.err => server_log.access */
652  }
653 
654  er_Accesslog_filename = er_Accesslog_filename_buff;
655 
656  /* in case of strlen(er_Msglog_filename) > PATH_MAX - 7 */
657  if (strnlen (er_Accesslog_filename_buff, PATH_MAX) >= PATH_MAX)
658  {
659  er_Accesslog_filename = NULL;
660  }
661 }
662 #endif /* SERVER_MODE || SA_MODE */
663 
664 /*
665  * er_init - Initialize parameters for message module
666  * return: none
667  * msglog_filename(in): name of message log file
668  * exit_ask(in): define behavior when a sever error is found
669  *
670  * Note: This function initializes parameters that define the behavior
671  * of the message module (i.e., logging file , and fatal error
672  * exit condition). If the value of msglog_filename is NULL,
673  * error messages are logged/sent to PRM_ER_LOG_FILE. If that
674  * is null, then messages go to stderr. If msglog_filename
675  * is equal to /dev/null, error messages are not logged.
676  */
677 int
678 er_init (const char *msglog_filename, int exit_ask)
679 {
680  int i;
681  const char *msg;
683 
684  if (er_Has_sticky_init)
685  {
686  assert (er_Hasalready_initiated);
687  // do not reinitialize
688  return NO_ERROR;
689  }
690 
691  if (er_Hasalready_initiated)
692  {
694  }
695 
696  for (i = 0; i < (int) DIM (er_Builtin_msg); i++)
697  {
698  if (er_Cached_msg[i] && er_Cached_msg[i] != er_Builtin_msg[i])
699  {
700  free_and_init (er_Cached_msg[i]);
701  }
702  er_Cached_msg[i] = (char *) er_Builtin_msg[i];
703  }
704 
705  assert (DIM (er_Fmt_list) > abs (ER_LAST_ERROR));
706 
707  for (i = 0; i < abs (ER_LAST_ERROR); i++)
708  {
709  er_init_fmt (&er_Fmt_list[i]);
710  }
711 
713  if (msg_catd != NULL)
714  {
715  er_Fmt_msg_fail_count = 0;
716  for (i = 2; i < abs (ER_LAST_ERROR); i++)
717  {
718  msg = msgcat_gets (msg_catd, MSGCAT_SET_ERROR, i, NULL);
719  if (msg == NULL || msg[0] == '\0')
720  {
721  er_Fmt_msg_fail_count++;
722  continue;
723  }
724  else
725  {
726  if (er_create_fmt_msg (&er_Fmt_list[i], -i, msg) == NULL)
727  {
728  er_Fmt_msg_fail_count++;
729  }
730  }
731  }
732  }
733  else
734  {
735  er_Fmt_msg_fail_count = abs (ER_LAST_ERROR) - 2;
736  }
737 
738  er_Hasalready_initiated = true;
739  // quick-fix for reloading error manager issues. the broker generated cas client need to reload error manager with
740  // update file name parameter. however, csql and other utilities should keep their initial file name.
741  // we need to treat this case properly. for now, I just consider "sticky" initialization when a specific file name
742  // is provided as argument. error manager is not reloaded after.
743  // for cas case, error manager is first initialized without a specific filename, so it won't be "sticky". It is
744  // reloaded during boot_register_client.
745  er_Has_sticky_init = (msglog_filename != NULL);
746 
747 #if !defined (SERVER_MODE)
748  // we need to register a context
749  er_Singleton_context_p = new context ();
750  er_Singleton_context_p->register_thread_local ();
751  // SERVER_MODE should have already one
752 #endif // not SERVER_MODE
753 
754  switch (exit_ask)
755  {
756  case ER_EXIT_ASK:
757  case ER_EXIT_DONT_ASK:
758  case ER_NEVER_EXIT:
759  case ER_ABORT:
760  er_Exit_ask = exit_ask;
761  break;
762 
763  default:
764  er_Exit_ask = ER_EXIT_DEFAULT;
765  er_log_debug (ARG_FILE_LINE, er_Cached_msg[ER_LOG_ASK_VALUE], exit_ask,
766  ((er_Exit_ask == ER_EXIT_ASK) ? "ER_EXIT_ASK" : "ER_NEVER_EXIT"));
767  break;
768  }
769 
770  /*
771  * Install event handler
772  */
774  {
775  if (er_event_init () != NO_ERROR)
776  {
778  }
779  }
780 
781  /*
782  * Remember the name of the message log file
783  */
784  if (msglog_filename == NULL)
785  {
787  {
788  msglog_filename = prm_get_string_value (PRM_ID_ER_LOG_FILE);
789  }
790  }
791 
792  // *INDENT-OFF*
793  // protect log file mutex
794  std::unique_lock<std::mutex> log_file_lock (er_Log_file_mutex); // mutex is released on destructor
795  // *INDENT-ON*
796 
797  if (msglog_filename != NULL)
798  {
799  if (IS_ABS_PATH (msglog_filename) || msglog_filename[0] == PATH_CURRENT)
800  {
801  strncpy (er_Msglog_filename_buff, msglog_filename, PATH_MAX - 1);
802  }
803  else
804  {
805  envvar_logdir_file (er_Msglog_filename_buff, PATH_MAX, msglog_filename);
806  }
807 
808  er_Msglog_filename_buff[PATH_MAX - 1] = '\0';
809  er_Msglog_filename = er_Msglog_filename_buff;
810  }
811  else
812  {
813  er_Msglog_filename = NULL;
814  }
815 
816 #if defined (SERVER_MODE) || defined (SA_MODE)
817  er_set_access_log_filename ();
818 #endif /* SERVER_MODE || SA_MODE */
819 
820  /* Define message log file */
821  if (er_Logfile_opened == false)
822  {
823  if (er_Msglog_filename)
824  {
825  er_Isa_null_device = er_file_isa_null_device (er_Msglog_filename);
826 
827  if (er_Isa_null_device)
828  {
829  er_Msglog_fh = er_file_open (er_Msglog_filename);
830  }
831  else
832  {
833  char path[PATH_MAX];
834  const char *er_file_path;
835 
837  {
838  er_file_path = er_Msglog_filename;
839  }
840  else
841  {
842  /* want to err on the side of doing production style error logs because this may be getting set at some
843  * naive customer site. */
844  sprintf (path, "%s.%d", er_Msglog_filename, getpid ());
845  er_file_path = path;
846  }
847 
848  er_Msglog_fh = er_file_open (er_file_path);
849 
850 #if !defined (WINDOWS) && defined (SERVER_MODE)
851  if (er_Msglog_fh != NULL)
852  {
853  er_file_create_link_to_current_log_file (er_file_path, ER_MSG_LOG_FILE_SUFFIX);
854  }
855 #endif /* !WINDOWS && SERVER_MODE */
856  }
857 
858  if (er_Msglog_fh == NULL)
859  {
860  er_Msglog_fh = stderr;
861  er_log_debug (ARG_FILE_LINE, er_Cached_msg[ER_LOG_MSGLOG_WARNING], er_Msglog_filename);
862  }
863  }
864  else
865  {
866  er_Msglog_fh = stderr;
867  }
868 
869  if (er_Accesslog_filename)
870  {
871  er_Isa_null_device = er_file_isa_null_device (er_Accesslog_filename);
872 
873  if (er_Isa_null_device)
874  {
875  er_Accesslog_fh = er_file_open (er_Accesslog_filename);
876  }
877  else
878  {
879  char path[PATH_MAX];
880  const char *ac_file_path;
881 
883  {
884  ac_file_path = er_Accesslog_filename;
885  }
886  else
887  {
888  /* want to err on the side of doing production style error logs because this may be getting set at some
889  * naive customer site. */
890  sprintf (path, "%s.%d", er_Accesslog_filename, getpid ());
891  ac_file_path = path;
892  }
893 
894  er_Accesslog_fh = er_file_open (ac_file_path);
895 
896 #if !defined (WINDOWS) && defined (SERVER_MODE)
897  if (er_Accesslog_fh != NULL)
898  {
899  er_file_create_link_to_current_log_file (ac_file_path, ER_ACCESS_LOG_FILE_SUFFIX);
900  }
901 #endif /* !WINDOWS && SERVER_MODE */
902  }
903 
904  if (er_Accesslog_fh == NULL)
905  {
906  er_Accesslog_fh = stderr;
907  er_log_debug (ARG_FILE_LINE, er_Cached_msg[ER_LOG_MSGLOG_WARNING], er_Accesslog_filename);
908  }
909  }
910  else
911  {
912  er_Accesslog_fh = stderr;
913  }
914 
915  er_Logfile_opened = true;
916  }
917 
918  log_file_lock.unlock ();
919 
920  /*
921  * Message catalog may be initialized by msgcat_init() during bootstrap.
922  * But, try once more to call msgcat_init() because there could be
923  * an exception case that get here before bootstrap.
924  */
925  int status = NO_ERROR;
926  if (msgcat_init () != NO_ERROR)
927  {
928  // todo
929  er_emergency (__FILE__, __LINE__, er_Cached_msg[ER_ER_NO_CATALOG]);
930  status = ER_FAILED;
931  }
932  else if (er_Is_cached_msg == false)
933  {
934  /* cache the messages */
935 
936  /*
937  * Remember, we skip code 0. If we can't find enough memory to
938  * copy the silly message, things are going to be pretty tight
939  * anyway, so just use the default version.
940  */
941  for (i = 1; i < (int) DIM (er_Cached_msg); i++)
942  {
943  const char *msg;
944  char *tmp;
945 
947  if (msg && *msg)
948  {
949  tmp = (char *) malloc (std::strlen (msg) + 1);
950  if (tmp)
951  {
952  strcpy (tmp, msg);
953  er_Cached_msg[i] = tmp;
954  }
955  }
956  }
957 
958  er_Is_cached_msg = true;
959  }
960 
961  if (er_call_stack_init () != NO_ERROR)
962  {
963  status = ER_FAILED;
964  }
965 
966  return status;
967 }
968 
969 /*
970  * er_is_initialized () - return if error manager was initialized.
971  *
972  * return :
973  * void (in) :
974  */
975 bool
977 {
979 }
980 
981 /*
982  * er_set_print_property -
983  * return: void
984  * print_console(in):
985  */
986 void
987 er_set_print_property (int print_console)
988 {
989  er_Print_to_console = print_console;
990 }
991 
992 /*
993  * er_file_open - small utility function to open error log file
994  * return: FILE *
995  * path(in): file path
996  */
997 static FILE *
998 er_file_open (const char *path)
999 {
1000  FILE *fp;
1001  char dir[PATH_MAX], *tpath;
1002 
1003  assert (path != NULL);
1004 
1005  tpath = strdup (path);
1006  while (1)
1007  {
1008  if (cub_dirname_r (tpath, dir, PATH_MAX) > 0 && access (dir, F_OK) < 0)
1009  {
1010  if (mkdir (dir, 0777) < 0 && errno == ENOENT)
1011  {
1012  free_and_init (tpath);
1013  tpath = strdup (dir);
1014  continue;
1015  }
1016  }
1017  break;
1018  }
1019  free_and_init (tpath);
1020 
1021  /* note: in "a+" mode, output is always appended */
1022  fp = fopen (path, "r+");
1023  if (fp != NULL)
1024  {
1025  fseek (fp, 0, SEEK_END);
1026  if (ftell (fp) > prm_get_integer_value (PRM_ID_ER_LOG_SIZE))
1027  {
1028  /* not a null device file */
1029  fp = er_file_backup (fp, path);
1030  }
1031  }
1032  else
1033  {
1034  fp = fopen (path, "w");
1035  }
1036  return fp;
1037 }
1038 
1039 /*
1040  * er_file_isa_null_device - check if it is a null device
1041  * return: true if the path is a null device. false otherwise
1042  * path(in): path to the file
1043  */
1044 static bool
1045 er_file_isa_null_device (const char *path)
1046 {
1047 #if defined(WINDOWS)
1048  const char *null_dev = "NUL";
1049 #else /* UNIX family */
1050  const char *null_dev = "/dev/null";
1051 #endif
1052 
1053  if (path != NULL && strcmp (path, null_dev) == 0)
1054  {
1055  return true;
1056  }
1057  else
1058  {
1059  return false;
1060  }
1061 }
1062 
1063 static FILE *
1064 er_file_backup (FILE * fp, const char *path)
1065 {
1066  char backup_file[PATH_MAX];
1067 
1068  assert (fp != NULL);
1069  assert (path != NULL);
1070 
1071  fclose (fp);
1072 
1073  sprintf (backup_file, "%s.bak", path);
1074  (void) unlink (backup_file);
1075  (void) rename (path, backup_file);
1076 
1077  return fopen (path, "w");
1078 }
1079 
1080 #if !defined (WINDOWS) && defined (SERVER_MODE)
1081 /*
1082  * er_file_create_link_to_current_log_file - creates a symbolic link to the current log file
1083  * return: none
1084  * log_file_path (in): path to the log file
1085  * suffix (in): file suffix
1086  *
1087  * The symbolic link is something like $CUBRID/log/server/{db_name}_latest{suffix}.
1088  */
1089 void
1090 er_file_create_link_to_current_log_file (const char *log_file_path, const char *suffix)
1091 {
1092  FILE *fp;
1093  char link_path[PATH_MAX];
1094  char link_dir_path[PATH_MAX];
1095  const char *db_name;
1096 
1097  assert (log_file_path != NULL);
1098 
1099  db_name = boot_db_name ();
1100  if (*db_name == '\0')
1101  {
1102  // boot in progress
1103  return;
1104  }
1105 
1106  cub_dirname_r (log_file_path, link_dir_path, PATH_MAX);
1107 
1108  if (snprintf (link_path, PATH_MAX, "%s%c%s_latest%s", link_dir_path, PATH_SEPARATOR, db_name, suffix) >= PATH_MAX)
1109  {
1110  // overflow
1111  return;
1112  }
1113 
1114  (void) unlink (link_path); // remove existing link file
1115  symlink (log_file_path, link_path);
1116 }
1117 #endif /* !WINDOWS && SERVER_MODE */
1118 
1119 /*
1120  * er_final - Terminate the error message module
1121  * return: none
1122  * do_global_final(in):
1123  * need_csect(in): server_mode only; if true, get ENTER_LOG_FILE critical section
1124  */
1125 void
1126 er_final (ER_FINAL_CODE do_global_final)
1127 {
1128  FILE *fh = NULL;
1129  int i = 0;
1130 
1131  if (!er_Hasalready_initiated)
1132  {
1133  // not initialized
1134  return;
1135  }
1136 
1137  if (do_global_final == ER_ALL_FINAL)
1138  {
1139 #if !defined (SERVER_MODE)
1140  // destroy singleton context
1141  er_Singleton_context_p->deregister_thread_local ();
1142  delete er_Singleton_context_p;
1143  er_Singleton_context_p = NULL;
1144 #endif // not SERVER_MODE
1145 
1146  er_event_final ();
1147 
1148  // *INDENT-OFF*
1149  // protect log file mutex
1150  std::unique_lock<std::mutex> log_file_lock (er_Log_file_mutex); // mutex is released on destructor
1151  // *INDENT-ON*
1152 
1153  if (er_Logfile_opened == true)
1154  {
1155  if (er_Msglog_fh != NULL && er_Msglog_fh != stderr)
1156  {
1157  fh = er_Msglog_fh;
1158  er_Msglog_fh = NULL;
1159  (void) fclose (fh);
1160  }
1161  er_Logfile_opened = false;
1162 
1163  if (er_Accesslog_fh != NULL && er_Accesslog_fh != stderr)
1164  {
1165  fh = er_Accesslog_fh;
1166  er_Accesslog_fh = NULL;
1167  (void) fclose (fh);
1168  }
1169  }
1170 
1171  log_file_lock.unlock ();
1172 
1173  for (i = 0; i < (int) DIM (er_Fmt_list); i++)
1174  {
1175  er_clear_fmt (&er_Fmt_list[i]);
1176  }
1177 
1178  for (i = 0; i < (int) DIM (er_Cached_msg); i++)
1179  {
1180  if (er_Cached_msg[i] != er_Builtin_msg[i])
1181  {
1182  free_and_init (er_Cached_msg[i]);
1183  er_Cached_msg[i] = (char *) er_Builtin_msg[i];
1184  }
1185  }
1186 
1188 
1189  er_Hasalready_initiated = false;
1190  er_Has_sticky_init = false;
1191  }
1192 }
1193 
1194 /*
1195  * er_clear - Clear any error message
1196  * return: none
1197  *
1198  * Note: This function is used to ignore an occurred error.
1199  */
1200 void
1201 er_clear (void)
1202 {
1203  if (!er_is_initialized ())
1204  {
1205  // ignore
1206  return;
1207  }
1208  context::get_thread_local_context ().clear_current_error_level ();
1209 }
1210 
1211 /*
1212  * er_set - Set an error
1213  * return: none
1214  * severity(in): may exit if severity == ER_FATAL_ERROR_SEVERITY
1215  * file_name(in): file name setting the error
1216  * line_no(in): line in file where the error is set
1217  * err_id(in): the error identifier
1218  * num_args(in): number of arguments...
1219  * ...(in): variable list of arguments (just like fprintf)
1220  *
1221  * Note: The error message associated with the given error identifier
1222  * is parsed and the arguments are substituted for later
1223  * retrieval. The caller must supply the exact number of
1224  * arguments to set all level messages of the error. The error
1225  * logging function (if any) associated with the error is called.
1226  */
1227 void
1228 er_set (int severity, const char *file_name, const int line_no, int err_id, int num_args, ...)
1229 {
1230  va_list ap;
1231 
1232  va_start (ap, num_args);
1233  (void) er_set_internal (severity, file_name, line_no, err_id, num_args, false, NULL, &ap);
1234  va_end (ap);
1235 }
1236 
1237 /*
1238  * er_set_with_file - Set an error and print file contents into
1239  * the error log file
1240  * return: none
1241  * severity(in): may exit if severity == ER_FATAL_ERROR_SEVERITY
1242  * file_name(in): file name setting the error
1243  * line_no(in): line in file where the error is set
1244  * err_id(in): the error identifier
1245  * fp(in): file pointer
1246  * num_args(in): number of arguments...
1247  * ...(in): variable list of arguments (just like fprintf)
1248  *
1249  * Note: The error message associated with the given error identifier
1250  * is parsed and the arguments are substituted for later
1251  * retrieval. The caller must supply the exact number of
1252  * arguments to set all level messages of the error. The error
1253  * logging function (if any) associated with the error is called.
1254  */
1255 void
1256 er_set_with_file (int severity, const char *file_name, const int line_no, int err_id, FILE * fp, int num_args, ...)
1257 {
1258  va_list ap;
1259 
1260  va_start (ap, num_args);
1261  (void) er_set_internal (severity, file_name, line_no, err_id, num_args, false, fp, &ap);
1262  va_end (ap);
1263 }
1264 
1265 /*
1266  * er_set_with_oserror - Set an error and include the OS last error
1267  * return: none
1268  * severity(in): may exit if severity == ER_FATAL_ERROR_SEVERITY
1269  * file_name(in): file name setting the error
1270  * line_no(in): line in file where the error is set
1271  * err_id(in): the error identifier
1272  * num_args(in): number of arguments...
1273  * ...(in): variable list of arguments (just like fprintf)
1274  *
1275  * Note: This function is the same as er_set + append Unix error message.
1276  * The error message associated with the given error identifier
1277  * is parsed and the arguments are substituted for later
1278  * retrieval. In addition the latest OS error message is appended
1279  * in all level messages for the error. The caller must supply
1280  * the exact number of arguments to set all level messages of the
1281  * error. The log error message function associated with the
1282  * error is called.
1283  */
1284 void
1285 er_set_with_oserror (int severity, const char *file_name, const int line_no, int err_id, int num_args, ...)
1286 {
1287  va_list ap;
1288 
1289  va_start (ap, num_args);
1290  (void) er_set_internal (severity, file_name, line_no, err_id, num_args, true, NULL, &ap);
1291  va_end (ap);
1292 }
1293 
1294 /*
1295  * er_notify_event_on_error - notify event
1296  * return: none
1297  * err_id(in):
1298  */
1299 static void
1301 {
1302  assert (err_id != NO_ERROR);
1303 
1304  err_id = abs (err_id);
1306  {
1307  er_event ();
1308  }
1309 }
1310 
1311 /*
1312  * er_print_callstack () - Print message with callstack (should help for
1313  * debug).
1314  *
1315  * return :
1316  * const char * file_name (in) :
1317  * const int line_no (in) :
1318  * const char * fmt (in) :
1319  * ... (in) :
1320  */
1321 void
1322 er_print_callstack (const char *file_name, const int line_no, const char *fmt, ...)
1323 {
1324  va_list ap;
1325 
1326  // *INDENT-OFF*
1327  // protect log file mutex
1328  std::unique_lock<std::mutex> log_file_lock (er_Log_file_mutex); // mutex is released on destructor
1329  // *INDENT-ON*
1330 
1331  va_start (ap, fmt);
1332  _er_log_debug_internal (file_name, line_no, fmt, &ap);
1333  va_end (ap);
1334 
1335  FILE *out = (er_Msglog_fh != NULL) ? er_Msglog_fh : stderr;
1336  er_dump_call_stack (out);
1337  fprintf (out, "\n");
1338 }
1339 
1340 /*
1341  * er_call_stack_dump_on_error - call stack dump
1342  * return: none
1343  * severity(in):
1344  * err_id(in):
1345  */
1346 static void
1348 {
1349  assert (err_id != NO_ERROR);
1350 
1351  err_id = abs (err_id);
1352  if (severity == ER_FATAL_ERROR_SEVERITY)
1353  {
1354  er_dump_call_stack (er_Msglog_fh);
1355  }
1357  {
1359  {
1360  er_dump_call_stack (er_Msglog_fh);
1361  }
1362  }
1363  else
1364  {
1366  {
1367  er_dump_call_stack (er_Msglog_fh);
1368  }
1369  }
1370 }
1371 
1372 /*
1373  * er_set_internal - Set an error and an optionally the Unix error
1374  * return:
1375  * severity(in): may exit if severity == ER_FATAL_ERROR_SEVERITY
1376  * file_name(in): file name setting the error
1377  * line_no(in): line in file where the error is set
1378  * err_id(in): the error identifier
1379  * num_args(in): number of arguments...
1380  * fp(in): file pointer
1381  * ...(in): variable list of arguments (just like fprintf)
1382  *
1383  * Note:
1384  */
1385 static int
1386 er_set_internal (int severity, const char *file_name, const int line_no, int err_id, int num_args,
1387  bool include_os_error, FILE * fp, va_list * ap_ptr)
1388 {
1389  va_list ap;
1390  const char *os_error;
1391  std::size_t new_size;
1392  ER_FMT *er_fmt = NULL;
1393  int ret_val = NO_ERROR;
1394  bool need_stack_pop = false;
1395 
1396  /* check if not used error code */
1397  assert (err_id != ER_TP_INCOMPATIBLE_DOMAINS);
1398  assert (err_id != ER_NUM_OVERFLOW);
1399  assert (err_id != ER_QPROC_OVERFLOW_COERCION);
1401  assert (err_id != ER_FK_CANT_DROP_CACHE_ATTR);
1402  assert (err_id != ER_SM_CATALOG_SPACE);
1403  assert (err_id != ER_CT_UNKNOWN_CLASSID);
1404  assert (err_id != ER_CT_REPRCNT_OVERFLOW);
1405 
1406  if (er_Hasalready_initiated == false)
1407  {
1408 #if defined (CS_MODE) && !defined (NDEBUG)
1409  /* temporary workaround for HA process which may encounter missing er_module */
1410  if (er_Ignore_uninit)
1411  {
1412  return ER_FAILED;
1413  }
1414 #endif
1415  assert (false);
1416  er_Errid_not_initialized = err_id;
1417  return ER_FAILED;
1418  }
1419 
1420  // *INDENT-OFF*
1421  context &tl_context = context::get_thread_local_context ();
1422  // *INDENT-ON*
1423 
1424  /*
1425  * Get the UNIX error message if needed. We need to get this as soon
1426  * as possible to avoid resetting the error.
1427  */
1428  os_error = (include_os_error && errno != 0) ? strerror (errno) : NULL;
1429 
1430  memcpy (&ap, ap_ptr, sizeof (ap));
1431 
1432  // *INDENT-OFF*
1433  // should force error stacking? yes if:
1434  // 1. this is a notification and an error was already set
1435  // 2. current error is interrupted error.
1436  er_message &prev_err = tl_context.get_current_error_level ();
1437  // *INDENT-ON*
1438 
1439  if ((severity == ER_NOTIFICATION_SEVERITY && prev_err.err_id != NO_ERROR) || (prev_err.err_id == ER_INTERRUPTED))
1440  {
1441  tl_context.push_error_stack ();
1442  need_stack_pop = true;
1443  }
1444 
1445  // *INDENT-OFF*
1446  // get current error reference
1447  er_message &crt_error = tl_context.get_current_error_level ();
1448  // *INDENT-ON*
1449 
1450  /* Initialize the area... */
1451  crt_error.set_error (err_id, severity, file_name, line_no);
1452 
1453  /*
1454  * Get hold of the compiled format string for this message and get an
1455  * estimate of the size of the buffer required to print it.
1456  */
1457  er_fmt = er_find_fmt (err_id, num_args);
1458  if (er_fmt == NULL)
1459  {
1460  /*
1461  * Assumes that er_find_fmt() has already called er_emergency().
1462  */
1463  ret_val = ER_FAILED;
1464  goto end;
1465  }
1466 
1467  if (err_id >= ER_FAILED || err_id <= ER_LAST_ERROR)
1468  {
1469  assert (0); /* invalid error id */
1470  err_id = ER_FAILED; /* invalid error id handling */
1471  }
1472 
1473  new_size = er_estimate_size (er_fmt, ap_ptr);
1474 
1475  /* Do we need to copy the OS error message? */
1476  if (os_error)
1477  {
1478  new_size += 4 + strlen (os_error);
1479  }
1480 
1481  /* Do any necessary allocation for the buffer. */
1482  crt_error.reserve_message_area (new_size + 1);
1483 
1484  /* And now format the silly thing. */
1485  if (er_vsprintf (&crt_error, er_fmt, ap_ptr) == ER_FAILED)
1486  {
1487  ret_val = ER_FAILED;
1488  goto end;
1489  }
1490  if (os_error != NULL)
1491  {
1492  strcat (crt_error.msg_area, "... ");
1493  strcat (crt_error.msg_area, os_error);
1494  }
1495 
1496  /* Call the logging function if any */
1497  if (severity <= prm_get_integer_value (PRM_ID_ER_LOG_LEVEL)
1498  && !(prm_get_bool_value (PRM_ID_ER_LOG_WARNING) == false && severity == ER_WARNING_SEVERITY)
1499  && er_Fnlog[severity] != NULL)
1500  {
1501  // *INDENT-OFF*
1502  // protect log file mutex
1503  std::unique_lock<std::mutex> log_file_lock (er_Log_file_mutex); // mutex is released on destructor
1504  // *INDENT-ON*
1505  (*er_Fnlog[severity]) (err_id);
1506 
1507  /* call stack dump */
1508  er_call_stack_dump_on_error (severity, err_id);
1509 
1510  /* event handler */
1511  er_notify_event_on_error (err_id);
1512 
1513  if (fp != NULL)
1514  {
1515  /* print file contents */
1516  if (fseek (fp, 0L, SEEK_SET) == 0)
1517  {
1518  char buf[MAX_LINE];
1519  while (fgets (buf, MAX_LINE, fp) != NULL)
1520  {
1521  fprintf (er_Msglog_fh, "%s", buf);
1522  }
1523  (void) fflush (er_Msglog_fh);
1524  }
1525  }
1526 
1527  log_file_lock.unlock ();
1528 
1529  if (er_Print_to_console && severity <= ER_ERROR_SEVERITY && crt_error.msg_area)
1530  {
1531  fprintf (stderr, "%s\n", crt_error.msg_area);
1532  }
1533  }
1534 
1535  /*
1536  * Do we want to stop the system on this error ... for debugging purposes?
1537  */
1539  {
1540  er_stop_on_error ();
1541  }
1542 
1543  if (severity == ER_NOTIFICATION_SEVERITY)
1544  {
1545  crt_error.clear_error ();
1546  }
1547 
1548 end:
1549 
1550  if (need_stack_pop)
1551  {
1552  tl_context.pop_error_stack_and_destroy ();
1553  }
1554 
1555  return ret_val;
1556 }
1557 
1558 /*
1559  * er_stop_on_error - Stop the system when an error occurs
1560  * return: none
1561  *
1562  * Note: This feature can be used when debugging a particular error outside the debugger. The user is asked whether or
1563  * not to continue.
1564  */
1565 static void
1567 {
1568  int exit_requested;
1569 
1570 #if !defined (WINDOWS)
1571  syslog (LOG_ALERT, er_Cached_msg[ER_STOP_SYSLOG], rel_name (), er_errid (), cuserid (NULL), getpid ());
1572 #endif /* !WINDOWS */
1573 
1574  (void) fprintf (stderr, "%s", er_Cached_msg[ER_ER_ASK]);
1575  if (scanf ("%d", &exit_requested) != 1)
1576  {
1577  exit_requested = TRUE;
1578  }
1579 
1580  if (exit_requested)
1581  {
1582  exit (EXIT_FAILURE);
1583  }
1584 }
1585 
1586 /*
1587  * er_log - Log the error message
1588  * return: none
1589  *
1590  * Note: The maximum available level of the error is logged into file.
1591  * This function is used at the default logging
1592  * function to call when error are set. The calling logging
1593  * function can be modified by calling the function er_fnerlog.
1594  */
1595 void
1597 {
1598  int severity;
1599  int nlevels;
1600  int line_no;
1601  const char *file_name;
1602  const char *msg;
1603  off_t position;
1604  time_t er_time;
1605  struct tm er_tm;
1606  struct tm *er_tm_p = &er_tm;
1607  struct timeval tv;
1608  char time_array[256];
1609  int tran_index;
1610  char *more_info_p;
1611  int ret;
1612  char more_info[CUB_MAXHOSTNAMELEN + PATH_MAX + 64];
1613  const char *log_file_name;
1614  const char *log_file_suffix;
1615  FILE **log_fh;
1616 
1617  if (er_Accesslog_filename != NULL && err_id == ER_BO_CLIENT_CONNECTED)
1618  {
1619  log_file_name = er_Accesslog_filename;
1620  log_file_suffix = ER_ACCESS_LOG_FILE_SUFFIX;
1621  log_fh = &er_Accesslog_fh;
1622  }
1623  else
1624  {
1625  log_file_name = er_Msglog_filename;
1626  log_file_suffix = ER_MSG_LOG_FILE_SUFFIX;
1627  log_fh = &er_Msglog_fh;
1628  }
1629 
1630  /* Check for an invalid output file */
1631  /* (stderr is not valid under Windows and is set to NULL) */
1632  if (log_fh == NULL || *log_fh == NULL)
1633  {
1634  return;
1635  }
1636 
1637  /* Make sure that we have a valid error identifier */
1638 
1639  /* Get the most detailed error message available */
1640  er_all (&err_id, &severity, &nlevels, &line_no, &file_name, &msg);
1641 
1642  /*
1643  * Don't let the file of log messages get very long. Backup or go back to the top if need be.
1644  */
1645  if (*log_fh != stderr && *log_fh != stdout && ftell (*log_fh) > (int) prm_get_integer_value (PRM_ID_ER_LOG_SIZE))
1646  {
1647  (void) fflush (*log_fh);
1648  (void) fprintf (*log_fh, "%s", er_Cached_msg[ER_LOG_WRAPAROUND]);
1649 
1650  if (!er_Isa_null_device)
1651  {
1652  *log_fh = er_file_backup (*log_fh, log_file_name);
1653 
1654  if (*log_fh == NULL)
1655  {
1656  *log_fh = stderr;
1657  er_log_debug (ARG_FILE_LINE, er_Cached_msg[ER_LOG_MSGLOG_WARNING], log_file_name);
1658  }
1659 #if !defined (WINDOWS) && defined (SERVER_MODE)
1660  else
1661  {
1662  er_file_create_link_to_current_log_file (log_file_name, log_file_suffix);
1663  }
1664 #endif /* !WINDOWS && SERVER_MODE */
1665  }
1666  else
1667  {
1668  /* Should be rewinded to avoid repeated limit check hitting */
1669  (void) fseek (*log_fh, 0L, SEEK_SET);
1670  }
1671  }
1672 
1673  if (*log_fh == stderr || *log_fh == stdout)
1674  {
1675  /*
1676  * Try to avoid out of sequence stderr & stdout.
1677  *
1678  */
1679  (void) fflush (stderr);
1680  (void) fflush (stdout);
1681  }
1682 
1683  /* LOG THE MESSAGE */
1684 
1685  er_time = time (NULL);
1686  er_tm_p = localtime_r (&er_time, &er_tm);
1687  if (er_tm_p == NULL)
1688  {
1689  strcpy (time_array, "00/00/00 00:00:00.000");
1690  }
1691  else
1692  {
1693  gettimeofday (&tv, NULL);
1694  snprintf (time_array + strftime (time_array, 128, "%m/%d/%y %H:%M:%S", er_tm_p), 255, ".%03ld",
1695  tv.tv_usec / 1000);
1696  }
1697 
1698  more_info_p = (char *) "";
1699 
1700  if (++er_Eid == 0)
1701  {
1702  er_Eid = 1;
1703  }
1704 
1705 #if defined (SERVER_MODE)
1706  {
1707  const char *prog_name = NULL;
1708  const char *user_name = NULL;
1709  const char *host_name = NULL;
1710  int pid = 0;
1711 
1712  if (logtb_find_client_tran_name_host_pid (tran_index, &prog_name, &user_name, &host_name, &pid) == NO_ERROR)
1713  {
1714  ret = snprintf (more_info, sizeof (more_info), ", CLIENT = %s:%s(%d), EID = %u",
1715  host_name ? host_name : "unknown", prog_name ? prog_name : "unknown", pid, er_Eid);
1716  if (ret > 0)
1717  {
1718  more_info_p = &more_info[0];
1719  }
1720  }
1721  }
1722 #else /* SERVER_MODE */
1723  tran_index = TM_TRAN_INDEX ();
1724  ret = snprintf (more_info, sizeof (more_info), ", EID = %u", er_Eid);
1725  if (ret > 0)
1726  {
1727  more_info_p = &more_info[0];
1728  }
1729 
1730  if (er_Handler != NULL)
1731  {
1732  (*er_Handler) (er_Eid);
1733  }
1734 #endif /* !SERVER_MODE */
1735 
1736  /* If file is not exist, it will recreate *log_fh file. */
1737  if ((access (log_file_name, F_OK) == -1) && *log_fh != stderr && *log_fh != stdout)
1738  {
1739  (void) fclose (*log_fh);
1740  *log_fh = er_file_open (log_file_name);
1741 
1742  if (*log_fh == NULL)
1743  {
1744  *log_fh = stderr;
1745  er_log_debug (ARG_FILE_LINE, er_Cached_msg[ER_LOG_MSGLOG_WARNING], log_file_name);
1746  }
1747  }
1748 
1749  fprintf (*log_fh, er_Cached_msg[ER_LOG_MSG_WRAPPER_D], time_array, ER_SEVERITY_STRING (severity), file_name, line_no,
1750  ER_ERROR_WARNING_STRING (severity), err_id, tran_index, more_info_p, msg);
1751  fflush (*log_fh);
1752 
1753  /* Flush the message so it is printed immediately */
1754  (void) fflush (*log_fh);
1755 
1756  if (*log_fh != stderr || *log_fh != stdout)
1757  {
1758  position = ftell (*log_fh);
1759  (void) fprintf (*log_fh, "%s", er_Cached_msg[ER_LOG_LAST_MSG]);
1760  (void) fflush (*log_fh);
1761  (void) fseek (*log_fh, position, SEEK_SET);
1762  }
1763 
1764  /* Do we want to exit ? */
1765 
1766  if (severity == ER_FATAL_ERROR_SEVERITY)
1767  {
1768  switch (er_Exit_ask)
1769  {
1770  case ER_ABORT:
1771  abort ();
1772  break;
1773 
1774  case ER_EXIT_ASK:
1775 #if defined (NDEBUG)
1776  er_stop_on_error ();
1777  break;
1778 #endif /* NDEBUG */
1779 
1780  case ER_EXIT_DONT_ASK:
1781  (void) fprintf (er_Msglog_fh, "%s", er_Cached_msg[ER_ER_EXIT]);
1782  (void) fflush (er_Msglog_fh);
1784  exit (EXIT_FAILURE);
1785  break;
1786 
1787  case ER_NEVER_EXIT:
1788  default:
1789  break;
1790  }
1791  }
1792 }
1793 
1796 {
1797 #if !defined (SERVER_MODE)
1798  er_log_handler_t prev;
1799 
1800  prev = er_Handler;
1801  er_Handler = handler;
1802  return prev;
1803 #else
1804  assert (0);
1805 
1806  return NULL;
1807 #endif
1808 }
1809 
1810 /*
1811  * er_errid - Retrieve last error identifier set before
1812  * return: error identifier
1813  *
1814  * Note: In most cases, it is simply enough to know whether or not
1815  * there was an error. However, in some cases it is convenient to
1816  * design the system and application to anticipate and handle
1817  * some errors.
1818  */
1819 int
1820 er_errid (void)
1821 {
1822  if (!er_Hasalready_initiated)
1823  {
1824 #if defined (CS_MODE) && !defined (NDEBUG)
1825  /* temporary workaround for HA process which may encounter missing er_module */
1826  if (er_Ignore_uninit)
1827  {
1828  return er_Errid_not_initialized;
1829  }
1830 #endif
1831  assert (false);
1832  return er_Errid_not_initialized;
1833  }
1834 
1835  return context::get_thread_local_error ().err_id;
1836 }
1837 
1838 /*
1839  * er_errid_if_has_error - Retrieve last error identifier set before
1840  * return: error identifier
1841  *
1842  * Note: The function ignores an error with ER_WARNING_SEVERITY or
1843  * ER_NOTIFICATION_SEVERITY.
1844  */
1845 int
1847 {
1848  // *INDENT-OFF*
1849  er_message &crt_error = context::get_thread_local_error ();
1850  // *INDENT-ON*
1851 
1852  return er_is_error_severity ((er_severity) crt_error.severity) ? crt_error.err_id : NO_ERROR;
1853 }
1854 
1855 /*
1856  * er_clearid - Clear only error identifier
1857  * return: none
1858  */
1859 void
1861 {
1862  // todo: is this necessary?
1863 #if defined (CS_MODE) && !defined (NDEBUG)
1864  /* temporary workaround for HA process which may encounter missing er_module */
1865  if (!er_Hasalready_initiated && er_Ignore_uninit)
1866  {
1867  return;
1868  }
1869 #endif
1870  assert (er_Hasalready_initiated);
1871 
1872  context::get_thread_local_error ().err_id = NO_ERROR;
1873 }
1874 
1875 /*
1876  * er_setid - Set only an error identifier
1877  * return: none
1878  * err_id(in):
1879  */
1880 void
1882 {
1883  // todo: is this necessary?
1884 #if defined (CS_MODE) && !defined (NDEBUG)
1885  /* temporary workaround for HA process which may encounter missing er_module */
1886  if (!er_Hasalready_initiated && er_Ignore_uninit)
1887  {
1888  return;
1889  }
1890 #endif
1891  assert (er_Hasalready_initiated);
1892 
1893  context::get_thread_local_error ().err_id = err_id;
1894 }
1895 
1896 /*
1897  * er_get_severity - Get severity of the last error set before
1898  * return: severity
1899  */
1900 int
1902 {
1903  return context::get_thread_local_error ().severity;
1904 }
1905 
1906 /*
1907  * er_has_error -
1908  * return: true if it has an actual error, otherwise false.
1909  * note: NOTIFICATION and WARNING are not regarded as an actual error.
1910  */
1911 bool
1913 {
1914  return er_is_error_severity ((er_severity) context::get_thread_local_error ().severity);
1915 }
1916 
1917 /*
1918  * er_msg - Retrieve current error message
1919  * return: a string, at the given level, describing the last error
1920  *
1921  * Note: The string returned is overwritten when the next error is set,
1922  * so it may be necessary to copy it to a static area if you
1923  * need to keep it for some length of time.
1924  */
1925 const char *
1926 er_msg (void)
1927 {
1928 #if defined (CS_MODE) && !defined (NDEBUG)
1929  /* temporary workaround for HA process which may encounter missing er_module */
1930  if (!er_Hasalready_initiated && er_Ignore_uninit)
1931  {
1932  return "Not available";
1933  }
1934 #endif
1935 
1936  if (!er_Hasalready_initiated)
1937  {
1938  assert (false);
1939  return "Not available"; // todo: is this safe?
1940  }
1941 
1942  // *INDENT-OFF*
1943  er_message &crt_error = context::get_thread_local_error ();
1944  // *INDENT-ON*
1945 
1946  if (crt_error.msg_area[0] == '\0')
1947  {
1948  std::strncpy (crt_error.msg_area, er_Cached_msg[ER_ER_MISSING_MSG], crt_error.msg_area_size);
1949  crt_error.msg_area[crt_error.msg_area_size - 1] = '\0';
1950  }
1951 
1952  return crt_error.msg_area;
1953 }
1954 
1955 /*
1956  * er_all - Return everything about the last error
1957  * return: none
1958  * err_id(out): error identifier
1959  * severity(out): severity of the error
1960  * n_levels(out): number of levels of the error
1961  * line_no(out): line number in the file where the error was set
1962  * file_name(out): file name where the error was set
1963  * error_msg(out): the formatted message of the error
1964  *
1965  * Note:
1966  */
1967 void
1968 er_all (int *err_id, int *severity, int *n_levels, int *line_no, const char **file_name, const char **error_msg)
1969 {
1970  // *INDENT-OFF*
1971  er_message &crt_error = context::get_thread_local_error ();
1972  // *INDENT-ON*
1973 
1974  *err_id = crt_error.err_id;
1975  *severity = crt_error.severity;
1976  *n_levels = 1; // is this stack level?
1977  *line_no = crt_error.line_no;
1978  *file_name = crt_error.file_name;
1979  *error_msg = er_msg ();
1980 }
1981 
1982 /*
1983  * _er_log_debug () - Print message to error log file.
1984  *
1985  * return : Void.
1986  * file_name (in) : __FILE__
1987  * line_no (in) : __LINE__
1988  * fmt (in) : Formatted message.
1989  * ... (in) : Arguments for formatted message.
1990  */
1991 void
1992 _er_log_debug (const char *file_name, const int line_no, const char *fmt, ...)
1993 {
1994  va_list ap;
1995 
1996 #if defined (CS_MODE) && !defined (NDEBUG)
1997  /* temporary workaround for HA process which may encounter missing er_module */
1998  if (!er_Hasalready_initiated && er_Ignore_uninit)
1999  {
2000  return;
2001  }
2002 #endif
2003 
2004  assert (er_Hasalready_initiated);
2005 
2006  // *INDENT-OFF*
2007  // protect log file mutex
2008  std::unique_lock<std::mutex> log_file_lock (er_Log_file_mutex); // mutex is released on destructor
2009  // *INDENT-ON*
2010 
2011  va_start (ap, fmt);
2012  _er_log_debug_internal (file_name, line_no, fmt, &ap);
2013  va_end (ap);
2014 }
2015 
2016 /*
2017  * er_log_debug - Print debugging message to the log file
2018  * return: none
2019  * file_name(in):
2020  * line_no(in):
2021  * fmt(in):
2022  * ap(in):
2023  *
2024  * Note:
2025  */
2026 static void
2027 _er_log_debug_internal (const char *file_name, const int line_no, const char *fmt, va_list * ap)
2028 {
2029  FILE *out;
2030  time_t er_time;
2031  struct tm er_tm;
2032  struct tm *er_tm_p = &er_tm;
2033  struct timeval tv;
2034  char time_array[256];
2035 
2036  if (er_Hasalready_initiated == false)
2037  {
2038  /* do not print debug info */
2039  return;
2040  }
2041 
2042  out = (er_Msglog_fh != NULL) ? er_Msglog_fh : stderr;
2043 
2044  er_time = time (NULL);
2045 
2046  er_tm_p = localtime_r (&er_time, &er_tm);
2047  if (er_tm_p == NULL)
2048  {
2049  strcpy (time_array, "00/00/00 00:00:00.000");
2050  }
2051  else
2052  {
2053  gettimeofday (&tv, NULL);
2054  snprintf (time_array + strftime (time_array, 128, "%m/%d/%y %H:%M:%S", er_tm_p), 255, ".%03ld",
2055  tv.tv_usec / 1000);
2056  }
2057 
2058  fprintf (out, er_Cached_msg[ER_LOG_DEBUG_NOTIFY], time_array, file_name, line_no);
2059 
2060  /* Print out remainder of message */
2061  vfprintf (out, fmt, *ap);
2062  fflush (out);
2063 }
2064 
2065 /*
2066  * er_get_ermsg_from_area_error -
2067  * return: ermsg string from the flatten error area
2068  * buffer(in): flatten error area
2069  */
2070 char *
2072 {
2073  assert (buffer != NULL);
2074 
2075  return buffer + (OR_INT_SIZE * 3);
2076 }
2077 
2078 /*
2079  * er_get_area_error - Flatten error information into an area
2080  * return: packed error information that can be transmitted to the client
2081  * length(out): length of the flatten area (set as a side effect)
2082  *
2083  * Note: This function is used for Client/Server transfering of errors.
2084  */
2085 char *
2086 er_get_area_error (char *buffer, int *length)
2087 {
2088  std::size_t len, max_msglen;
2089  char *ptr;
2090  const char *msg;
2091 
2092  assert (*length > OR_INT_SIZE * 3);
2093 
2094  // *INDENT-OFF*
2095  er_message &crt_error = context::get_thread_local_error ();
2096  // *INDENT-ON*
2097 
2098  /* Now copy the information */
2099  msg = strlen (crt_error.msg_area) != 0 ? crt_error.msg_area : "(null)";
2100 
2101  len = (OR_INT_SIZE * 3) + strlen (msg) + 1;
2102  len = MIN (len, *length);
2103  *length = (int) len;
2104  max_msglen = len - (OR_INT_SIZE * 3) - 1;
2105 
2106  ptr = buffer;
2107  ASSERT_ALIGN (ptr, INT_ALIGNMENT);
2108 
2109  OR_PUT_INT (ptr, (int) crt_error.err_id);
2110  ptr += OR_INT_SIZE;
2111 
2112  OR_PUT_INT (ptr, (int) crt_error.severity);
2113  ptr += OR_INT_SIZE;
2114 
2115  OR_PUT_INT (ptr, len);
2116  ptr += OR_INT_SIZE;
2117 
2118  strncpy (ptr, msg, max_msglen);
2119  *(ptr + max_msglen) = '\0'; /* bullet proofing */
2120 
2121  return buffer;
2122 }
2123 
2124 /*
2125  * er_set_area_error - Reset the error information
2126  * return: error id which was contained in the given error information
2127  * server_area(in): the flatten area with error information
2128  *
2129  * Note: Error information is reset with the one provided by the packed area,
2130  * which is the last error found in the server.
2131  */
2132 int
2133 er_set_area_error (char *server_area)
2134 {
2135  char *ptr;
2136  int err_id, severity;
2137  std::size_t length = 0;
2138 
2139  if (server_area == NULL)
2140  {
2141  er_clear ();
2142  return NO_ERROR;
2143  }
2144 
2145  // *INDENT-OFF*
2146  er_message &crt_error = context::get_thread_local_error ();
2147  // *INDENT-ON*
2148 
2149  ptr = server_area;
2150  ASSERT_ALIGN (ptr, INT_ALIGNMENT);
2151 
2152  err_id = OR_GET_INT (ptr);
2153  ptr += OR_INT_SIZE;
2154  assert (err_id <= NO_ERROR && ER_LAST_ERROR < err_id);
2155 
2156  severity = OR_GET_INT (ptr);
2157  ptr += OR_INT_SIZE;
2158  assert (ER_FATAL_ERROR_SEVERITY <= severity && severity <= ER_MAX_SEVERITY);
2159 
2160  length = OR_GET_INT (ptr);
2161  ptr += OR_INT_SIZE;
2162 
2163  crt_error.err_id = ((err_id >= 0 || err_id <= ER_LAST_ERROR) ? -1 : err_id);
2164  crt_error.severity = severity;
2165  crt_error.file_name = "Unknown from server";
2166  crt_error.line_no = -1;
2167 
2168  /* Note, current length is the length of the packet not the string, considering that this is NULL terminated, we
2169  * don't really need to be sending this. Use the actual string length in the memcpy here! */
2170  length = strlen (ptr) + 1;
2171 
2172  crt_error.reserve_message_area (length);
2173  memcpy (crt_error.msg_area, ptr, length);
2174 
2175  /* Call the logging function if any */
2176  if (severity <= prm_get_integer_value (PRM_ID_ER_LOG_LEVEL)
2177  && !(prm_get_bool_value (PRM_ID_ER_LOG_WARNING) == false && severity == ER_WARNING_SEVERITY)
2178  && er_Fnlog[severity] != NULL)
2179  {
2180  // *INDENT-OFF*
2181  // protect log file mutex
2182  std::unique_lock<std::mutex> log_file_lock (er_Log_file_mutex); // mutex is released on destructor
2183  // *INDENT-ON*
2184 
2185  (*er_Fnlog[severity]) (err_id);
2186  log_file_lock.unlock ();
2187 
2188  if (er_Print_to_console && severity <= ER_ERROR_SEVERITY && crt_error.msg_area)
2189  {
2190  fprintf (stderr, "%s\n", crt_error.msg_area);
2191  }
2192  }
2193 
2194  return crt_error.err_id;
2195 }
2196 
2197 /*
2198  * er_stack_push - Save the current error onto the stack
2199  * return: NO_ERROR or ER_FAILED
2200  *
2201  * Note: The current set error information is saved onto a stack.
2202  * This function can be used in conjunction with er_stack_pop when
2203  * the caller function wants to return the current error message
2204  * no matter what other additional errors are set. For example,
2205  * a function may detect an error, then call another function to
2206  * do some cleanup. If the cleanup function set an error, the
2207  * desired error can be lost.
2208  * A function that push something should pop or clear the entry,
2209  * otherwise, a function doing a pop may not get the right entry.
2210  */
2211 void
2213 {
2214  (void) context::get_thread_local_context ().push_error_stack ();
2215 }
2216 
2217 /*
2218  * er_stack_push_if_exists - Save the last error if exists onto the stack
2219  *
2220  * Note: Please notice the difference from er_stack_push.
2221  * This function only pushes when an error was set, while er_stack_push always makes a room
2222  * and pushes the current entry. It will be used in conjunction with er_restore_last_error.
2223  */
2224 void
2226 {
2227  // *INDENT-OFF*
2228  context &tl_context = context::get_thread_local_context ();
2229  // *INDENT-ON*
2230 
2231  if (tl_context.get_current_error_level ().err_id == NO_ERROR && !tl_context.has_error_stack ())
2232  {
2233  /* If neither an error was set nor pushed, keep using the current error entry.
2234  *
2235  * If this is not a base error entry,
2236  * we have to push one since it means that caller pushed one and latest entry will be soon popped.
2237  */
2238  return;
2239  }
2240 
2241  // push
2242  (void) tl_context.push_error_stack ();
2243 }
2244 
2245 /*
2246  * er_stack_pop - Restore the previous error from the stack.
2247  * The latest saved error is restored in the error area.
2248  * return: NO_ERROR or ER_FAILED
2249  */
2250 void
2252 {
2253  context::get_thread_local_context ().pop_error_stack_and_destroy ();
2254 }
2255 
2256 /*
2257  * er_stack_pop_and_keep_error - Clear the latest saved error message in the stack
2258  * That is, pop without restore.
2259  * return: none
2260  */
2261 void
2263 {
2264  // *INDENT-OFF*
2265  context &tl_context = context::get_thread_local_context ();
2266  // *INDENT-ON*
2267  er_message top (tl_context.get_logging ());
2268 
2269  if (!tl_context.has_error_stack ())
2270  {
2271  // bad pop
2272  assert (false);
2273  return;
2274  }
2275 
2276  tl_context.pop_error_stack (top);
2277  if (top.err_id != NO_ERROR)
2278  {
2279  /* keep the error. push it to current top */
2280  top.swap (tl_context.get_thread_local_error ());
2281  }
2282  else
2283  {
2284  // leave as is
2285  }
2286 
2287  // popped error stack is destroyed
2288 }
2289 
2290 /*
2291  * er_restore_last_error - Restore the last error between the current entry and the pushed one.
2292  * If the current entry has an error, clear the pushed entry which is no longer needed.
2293  * Otherwise, pop the current entry and restore the saved one.
2294  *
2295  * return: none
2296  *
2297  * Note: Please see also er_stack_push_if_exists
2298  */
2299 void
2301 {
2302  // *INDENT-OFF*
2303  context &tl_context = context::get_thread_local_context ();
2304  // *INDENT-ON*
2305 
2306  if (!tl_context.has_error_stack ())
2307  {
2308  /* When no pushed entry exists, keep using the current entry. */
2309  return;
2310  }
2311 
2313 }
2314 
2315 /*
2316  * er_stack_clearall - Clear all saved error messages
2317  * return: none
2318  */
2319 void
2321 {
2322  // *INDENT-OFF*
2323  context &tl_context = context::get_thread_local_context ();
2324  // *INDENT-ON*
2325 
2326  // remove all stacks, but keep last error
2327  while (tl_context.has_error_stack ())
2328  {
2330  }
2331 }
2332 
2333 
2334 /*
2335  * er_study_spec -
2336  * return: the length of the spec
2337  * conversion_spec(in): a single printf() conversion spec, without the '%'
2338  * simple_spec(out): a pointer to a buffer to receive a simple version of
2339  * the spec (one without a positional specifier)
2340  * position(out): the position of the spec
2341  * width(out): the nominal width of the field
2342  * va_class(out): a classification of the base (va_list)
2343  * type of the arguments described by the spec
2344  *
2345  * Note: Breaks apart the individual components of the conversion spec
2346  * (as described in the Sun man page) and sets the appropriate
2347  * buffers to record that info.
2348  */
2349 static int
2350 er_study_spec (const char *conversion_spec, char *simple_spec, int *position, int *width, int *va_class)
2351 {
2352  char *p;
2353  const char *q;
2354  int n, code, class_;
2355 
2356  code = 0;
2357  class_ = 0;
2358 
2359  simple_spec[0] = '%';
2360  p = &simple_spec[1];
2361  q = conversion_spec;
2362 
2363  /*
2364  * Skip leading flags...
2365  */
2366 
2367  while (*q == '-' || *q == '+' || *q == ' ' || *q == '#')
2368  {
2369  *p++ = *q++;
2370  }
2371 
2372  /*
2373  * Now look for a numeric field. This could be either a position
2374  * specifier or a width specifier; we won't know until we find out
2375  * what follows it.
2376  */
2377 
2378  n = 0;
2379  while (char_isdigit (*q))
2380  {
2381  n *= 10;
2382  n += (*q) - '0';
2383  *p++ = *q++;
2384  }
2385 
2386  if (*q == '$')
2387  {
2388  /*
2389  * The number was a position specifier, so record that, skip the
2390  * '$', and start over depositing conversion spec characters at
2391  * the beginning of simple_spec.
2392  */
2393  q++;
2394 
2395  if (n)
2396  {
2397  *position = n;
2398  }
2399  p = &simple_spec[1];
2400 
2401  /*
2402  * Look for flags again...
2403  */
2404  while (*q == '-' || *q == '+' || *q == ' ' || *q == '#')
2405  {
2406  *p++ = *q++;
2407  }
2408 
2409  /*
2410  * And then look for a width specifier...
2411  */
2412  n = 0;
2413  while (char_isdigit (*q))
2414  {
2415  n *= 10;
2416  n += (*q) - '0';
2417  *p++ = *q++;
2418  }
2419  *width = n;
2420  }
2421 
2422  /*
2423  * Look for an optional precision...
2424  */
2425  if (*q == '.')
2426  {
2427  *p++ = *q++;
2428  while (char_isdigit (*q))
2429  {
2430  *p++ = *q++;
2431  }
2432  }
2433 
2434  /*
2435  * And then for modifier flags...
2436  */
2437  if (*q == 'l' && *(q + 1) == 'l')
2438  {
2439  /* long long type */
2440  class_ = SPEC_CODE_LONGLONG;
2441  *p++ = *q++;
2442  *p++ = *q++;
2443  }
2444  else if (*q == 'z')
2445  {
2446  /* size_t type */
2447  class_ = SPEC_CODE_SIZE_T;
2448 #if defined (WINDOWS)
2449  *p++ = 'I';
2450  q++;
2451 #elif defined (__GNUC__)
2452  *p++ = *q++;
2453 #else
2454  if (sizeof (size_t) == sizeof (long long int))
2455  {
2456  *p++ = 'l';
2457  *p++ = 'l';
2458  }
2459  else
2460  {
2461  /* no size modifier */
2462  }
2463  q++;
2464 #endif /* WINDOWS */
2465  }
2466  else if (*q == 'h')
2467  {
2468  /*
2469  * Ignore this spec and use the class determined (later) by
2470  * examining the coversion code. According to Plauger, the
2471  * standard dictates that stdarg.h be implemented so that short
2472  * values are all coerced to int.
2473  */
2474  *p++ = *q++;
2475  }
2476 
2477  /*
2478  * Now copy the actual conversion code.
2479  */
2480  code = *p++ = *q++;
2481  *p++ = '\0';
2482 
2483  if (class_ == 0)
2484  {
2485  switch (code)
2486  {
2487  case 'c':
2488  case 'd':
2489  case 'i':
2490  case 'o':
2491  case 'u':
2492  case 'x':
2493  case 'X':
2494  class_ = 'i';
2495  break;
2496  case 'p':
2497  class_ = 'p';
2498  break;
2499  case 'e':
2500  case 'f':
2501  case 'g':
2502  case 'E':
2503  case 'F':
2504  case 'G':
2505  class_ = 'f';
2506  break;
2507  case 's':
2508  class_ = 's';
2509  break;
2510  default:
2511  assert (0);
2512  er_log_debug (ARG_FILE_LINE, er_Cached_msg[ER_LOG_UNKNOWN_CODE], code);
2513  break;
2514  }
2515  }
2516  *va_class = class_;
2517 
2518  return CAST_STRLEN (q - conversion_spec);
2519 }
2520 
2521 /*
2522  * er_study_fmt -
2523  * return:
2524  * fmt(in/out): a pointer to an ER_FMT structure to be initialized
2525  *
2526  * Note: Scans the printf format string in fmt->fmt and compiles
2527  * interesting information about the conversion specs in the
2528  * string into the spec[] array.
2529  */
2530 static void
2532 {
2533  const char *p;
2534  int width, va_class;
2535  char buf[10];
2536 
2537  int i, n;
2538 
2539  fmt->nspecs = 0;
2540  for (p = strchr (fmt->fmt, '%'); p; p = strchr (p, '%'))
2541  {
2542  if (p[1] == '%')
2543  { /* " ...%%..." ??? */
2544  p += 1;
2545  }
2546  else
2547  {
2548  /*
2549  * Set up the position parameter off by one so that we can
2550  * decrement it without checking later.
2551  */
2552  n = ++fmt->nspecs;
2553  width = 0;
2554  va_class = 0;
2555 
2556  p += er_study_spec (&p[1], buf, &n, &width, &va_class);
2557 
2558  /*
2559  * 'n' may have been modified by er_study_spec() if we ran
2560  * into a conversion spec with a positional component (e.g.,
2561  * %3$d).
2562  */
2563  n -= 1;
2564 
2565  if (n >= fmt->spec_top)
2566  {
2567  ER_SPEC *new_spec;
2568  int size;
2569 
2570  /*
2571  * Grow the conversion spec array.
2572  */
2573  size = (n + 1) * sizeof (ER_SPEC);
2574  new_spec = (ER_SPEC *) ER_MALLOC (size);
2575  if (new_spec == NULL)
2576  return;
2577  memcpy (new_spec, fmt->spec, fmt->spec_top * sizeof (ER_SPEC));
2578  if (fmt->spec != fmt->spec_buf)
2579  free_and_init (fmt->spec);
2580  fmt->spec = new_spec;
2581  fmt->spec_top = (n + 1);
2582  }
2583 
2584  strcpy (fmt->spec[n].spec, buf);
2585  fmt->spec[n].code = va_class;
2586  fmt->spec[n].width = width;
2587  }
2588  }
2589 
2590  /*
2591  * Make sure that there were no "holes" left in the parameter space
2592  * (e.g., "%1$d" and "%3$d", but no "%2$d" spec), and that there were
2593  * no unknown conversion codes. If there was a problem, we can't
2594  * count on being able to safely decode the va_list we'll get, and
2595  * we're better off just printing a generic message that requires no
2596  * formatting.
2597  */
2598  for (i = 0; i < fmt->nspecs; i++)
2599  {
2600  if (fmt->spec[i].code == 0)
2601  {
2602  int code;
2603  code = fmt->err_id;
2604  er_log_debug (ARG_FILE_LINE, er_Cached_msg[ER_LOG_SUSPECT_FMT], code);
2606  break;
2607  }
2608  }
2609 }
2610 
2611 #define MAX_INT_WIDTH 20
2612 #define MAX_DOUBLE_WIDTH 32
2613 /*
2614  * er_estimate_size -
2615  * return: a byte count
2616  * fmt(in/out): a pointer to an already-studied ER_FMT structure
2617  * ap(in): a va_list of arguments
2618  *
2619  * Note:
2620  * Uses the arg_spec[] info in *fmt, along with the actual args
2621  * in ap, to make a conservative guess of how many bytes will be
2622  * required by vsprintf().
2623  *
2624  * If fmt hasn't already been studied by er_study_fmt(), this
2625  * will yield total garbage, if it doesn't blow up.
2626  *
2627  * DOESN'T AFFECT THE CALLER'S VIEW OF THE VA_LIST.
2628  */
2629 static size_t
2630 er_estimate_size (ER_FMT * fmt, va_list * ap)
2631 {
2632  int i, width;
2633  size_t n;
2634  size_t len;
2635  va_list args;
2636  const char *str;
2637 
2638  /*
2639  * fmt->fmt can be NULL if something went wrong while studying it.
2640  */
2641  if (fmt->fmt == NULL)
2642  {
2643  return strlen (er_Cached_msg[ER_ER_SUBSTITUTE_MSG]);
2644  }
2645 
2646  memcpy (&args, ap, sizeof (args));
2647 
2648  len = fmt->fmt_length;
2649 
2650  for (i = 0; i < fmt->nspecs; i++)
2651  {
2652  switch (fmt->spec[i].code)
2653  {
2654  case 'i':
2655  (void) va_arg (args, int);
2656  n = MAX_INT_WIDTH;
2657  break;
2658 
2659  case SPEC_CODE_LONGLONG:
2660  (void) va_arg (args, long long int);
2661  n = MAX_INT_WIDTH;
2662  break;
2663 
2664  case SPEC_CODE_SIZE_T:
2665  if (sizeof (size_t) == sizeof (long long int))
2666  {
2667  (void) va_arg (args, long long int);
2668  }
2669  else
2670  {
2671  (void) va_arg (args, int);
2672  }
2673  n = MAX_INT_WIDTH;
2674  break;
2675 
2676  case 'p':
2677  (void) va_arg (args, void *);
2678  n = MAX_INT_WIDTH;
2679  break;
2680 
2681  case 'f':
2682  (void) va_arg (args, double);
2683  n = MAX_DOUBLE_WIDTH;
2684  break;
2685 
2686  case 'L':
2687  (void) va_arg (args, long double);
2688  n = MAX_DOUBLE_WIDTH;
2689  break;
2690 
2691  case 's':
2692  str = va_arg (args, char *);
2693  if (str == NULL)
2694  str = "(null)";
2695  n = strlen (str);
2696  break;
2697 
2698  default:
2699  er_log_debug (ARG_FILE_LINE, er_Cached_msg[ER_LOG_UNKNOWN_CODE], fmt->spec[i].code);
2700  /*
2701  * Pray for protection... We really shouldn't be able to get
2702  * here, since er_study_fmt() should protect us from it.
2703  */
2704  n = MAX_DOUBLE_WIDTH;
2705  break;
2706  }
2707  width = fmt->spec[i].width;
2708  len += MAX (width, n);
2709  }
2710 
2711  return len;
2712 }
2713 
2714 /*
2715  * er_find_fmt -
2716  * return: error formats
2717  * err_id(in): error identifier
2718  *
2719  * Note: "er_cache.lock" should have been acquired before calling this function.
2720  * And this thread should not release the mutex before return.
2721  */
2722 static ER_FMT *
2723 er_find_fmt (int err_id, int num_args)
2724 {
2725  const char *msg;
2726  ER_FMT *fmt;
2727 
2728  // *INDENT-OFF*
2729  // protect log file mutex
2730  std::unique_lock<std::mutex> log_msg_cache (er_Message_cache_mutex, std::defer_lock); // mutex is released on
2731  // destructor
2732  // *INDENT-ON*
2733 
2734  if (err_id < ER_FAILED && err_id > ER_LAST_ERROR)
2735  {
2736  fmt = &er_Fmt_list[-err_id];
2737  }
2738  else
2739  {
2740  assert (0);
2741  fmt = &er_Fmt_list[-ER_FAILED];
2742  }
2743 
2744  if (er_Fmt_msg_fail_count > 0)
2745  {
2746  log_msg_cache.lock ();
2747  }
2748 
2749  if (fmt->fmt == NULL)
2750  {
2751  assert (er_Fmt_msg_fail_count > 0);
2752 
2754  if (msg == NULL || msg[0] == '\0')
2755  {
2756  er_log_debug (ARG_FILE_LINE, er_Cached_msg[ER_ER_LOG_MISSING_MSG], err_id);
2757  msg = er_Cached_msg[ER_ER_MISSING_MSG];
2758  }
2759 
2760  fmt = er_create_fmt_msg (fmt, err_id, msg);
2761 
2762  if (fmt != NULL)
2763  {
2764  /*
2765  * Be sure that we have the same number of arguments before calling
2766  * er_estimate_size(). Because it uses straight va_arg() and friends
2767  * to grab its arguments, it is vulnerable to argument mismatches
2768  * (e.g., too few arguments, ints in string positions, etc). This
2769  * won't save us when someone passes an integer argument where the
2770  * format says to expect a string, but it will save us if someone
2771  * just plain forgets how many args there are.
2772  */
2773  if (fmt->nspecs != num_args)
2774  {
2775  er_log_debug (ARG_FILE_LINE, er_Cached_msg[ER_LOG_SUSPECT_FMT], err_id);
2776  er_internal_msg (fmt, err_id, ER_ER_SUBSTITUTE_MSG);
2777  }
2778  }
2779  er_Fmt_msg_fail_count--;
2780  }
2781 
2782  return fmt;
2783 }
2784 
2785 static ER_FMT *
2786 er_create_fmt_msg (ER_FMT * fmt, int err_id, const char *msg)
2787 {
2788  std::size_t msg_length;
2789 
2790  msg_length = strlen (msg);
2791 
2792  fmt->fmt = (char *) ER_MALLOC (msg_length + 1);
2793  if (fmt->fmt == NULL)
2794  {
2795  er_internal_msg (fmt, err_id, ER_ER_MISSING_MSG);
2796  return NULL;
2797  }
2798 
2799  fmt->fmt_length = (int) msg_length;
2800  fmt->must_free = 1;
2801 
2802  strcpy (fmt->fmt, msg);
2803 
2804  /*
2805  * Now study the format specs and squirrel away info about them.
2806  */
2807  fmt->err_id = err_id;
2808  er_study_fmt (fmt);
2809 
2810  return fmt;
2811 }
2812 
2813 /*
2814  * er_init_fmt -
2815  * return: none
2816  * fmt(in/out):
2817  */
2818 static void
2820 {
2821  fmt->err_id = 0;
2822  fmt->fmt = NULL;
2823  fmt->fmt_length = 0;
2824  fmt->must_free = 0;
2825  fmt->nspecs = 0;
2826  fmt->spec_top = DIM (fmt->spec_buf);
2827  fmt->spec = fmt->spec_buf;
2828 }
2829 
2830 /*
2831  * er_clear_fmt -
2832  * return: none
2833  * fmt(in/out):
2834  */
2835 static void
2837 {
2838  if (fmt->fmt && fmt->must_free)
2839  {
2840  free_and_init (fmt->fmt);
2841  }
2842  fmt->fmt = NULL;
2843  fmt->fmt_length = 0;
2844  fmt->must_free = 0;
2845 
2846  if (fmt->spec && fmt->spec != fmt->spec_buf)
2847  {
2848  free_and_init (fmt->spec);
2849  }
2850  fmt->spec = fmt->spec_buf;
2851  fmt->spec_top = DIM (fmt->spec_buf);
2852  fmt->nspecs = 0;
2853 }
2854 
2855 /*
2856  * er_internal_msg -
2857  * return:
2858  * fmt(in/out):
2859  * code(in):
2860  * msg_num(in):
2861  */
2862 static void
2863 er_internal_msg (ER_FMT * fmt, int code, int msg_num)
2864 {
2865  er_clear_fmt (fmt);
2866 
2867  fmt->err_id = code;
2868  fmt->fmt = (char *) er_Cached_msg[msg_num];
2869  fmt->fmt_length = (int) strlen (fmt->fmt);
2870  fmt->must_free = 0;
2871 }
2872 
2873 /*
2874  * er_malloc_helper -
2875  * return:
2876  * size(in):
2877  * file(in):
2878  * line(in):
2879  */
2880 static void *
2881 er_malloc_helper (std::size_t size, const char *file, int line)
2882 {
2883  void *mem;
2884 
2885  mem = malloc (size);
2886  if (mem == NULL)
2887  {
2888  er_emergency (file, line, er_Cached_msg[ER_ER_OUT_OF_MEMORY], size);
2889  }
2890 
2891  return mem;
2892 }
2893 
2894 /*
2895  * er_emergency - Does a poor man's sprintf()
2896  * which understands only '%s' and '%d'
2897  * return: none
2898  * file(in):
2899  * line(in):
2900  * fmt(in):
2901  * ...(in):
2902  *
2903  * Note: Do not malloc() any memory since this can be called
2904  * from low-memory situations
2905  */
2906 static void
2907 er_emergency (const char *file, int line, const char *fmt, ...)
2908 {
2909  va_list args;
2910  const char *str, *p, *q;
2911  int limit, span;
2912  char buf[32];
2913 
2914  // *INDENT-OFF*
2915  er_message &crt_error = context::get_thread_local_error ();
2916  // *INDENT-ON*
2917 
2918  crt_error.err_id = ER_GENERIC_ERROR;
2919  crt_error.severity = ER_ERROR_SEVERITY;
2920  crt_error.file_name = file;
2921  crt_error.line_no = line;
2922 
2923  /* it is assumed that default message buffer is big enough to hold this */
2924  sprintf (crt_error.msg_area, er_Cached_msg[ER_ER_HEADER], line);
2925  limit = (int) (crt_error.msg_area_size - strlen (crt_error.msg_area) - 1);
2926 
2927  va_start (args, fmt);
2928 
2929  p = fmt;
2930  crt_error.msg_area[0] = '\0';
2931  while ((q = strchr (p, '%')) && limit > 0)
2932  {
2933  /*
2934  * Copy the text between the last conversion spec and the next.
2935  */
2936  span = CAST_STRLEN (q - p);
2937  span = MIN (limit, span);
2938  strncat (crt_error.msg_area, p, span);
2939  p = q + 2;
2940  limit -= span;
2941 
2942  /*
2943  * Now convert and print the arg.
2944  */
2945  switch (q[1])
2946  {
2947  case 'd':
2948  sprintf (buf, "%d", va_arg (args, int));
2949  str = buf;
2950  break;
2951  case 'l':
2952  if (q[2] == 'd')
2953  {
2954  sprintf (buf, "%d", va_arg (args, int));
2955  str = buf;
2956  }
2957  else
2958  {
2959  str = "???";
2960  }
2961  break;
2962  case 's':
2963  str = va_arg (args, const char *);
2964  if (str == NULL)
2965  {
2966  str = "(null)";
2967  }
2968  break;
2969  case '%':
2970  str = "%";
2971  break;
2972  default:
2973  str = "???";
2974  break;
2975  }
2976  strncat (crt_error.msg_area, str, limit);
2977  limit -= (int) strlen (str);
2978  limit = MAX (limit, 0);
2979  }
2980 
2981  va_end (args);
2982 
2983  /*
2984  * Now copy the message text following the last conversion spec,
2985  * making sure that we null-terminate the buffer (since strncat won't
2986  * do it if it reaches the end of the buffer).
2987  */
2988  strncat (crt_error.msg_area, p, limit);
2989  crt_error.msg_area[crt_error.msg_area_size - 1] = '\0';
2990 
2991  /* Now get it into the log. */
2992  er_log (crt_error.err_id);
2993 }
2994 
2995 /*
2996  * er_vsprintf -
2997  * return:
2998  * fmt(in/out):
2999  * ap(in):
3000  */
3001 static int
3002 er_vsprintf (er_message * er_entry_p, ER_FMT * fmt, va_list * ap)
3003 {
3004  const char *p; /* The start of the current non-spec part of fmt */
3005  const char *q; /* The start of the next conversion spec */
3006  char *s; /* The end of the valid part of er_entry_p->msg_area */
3007  int n; /* The va_list position of the current arg */
3008  int i;
3009  va_list args;
3010 
3011  assert (er_entry_p != NULL);
3012 
3013  /*
3014  * *** WARNING ***
3015  *
3016  * This routine assumes that er_entry_p->msg_area is large enough to
3017  * receive the message being formatted. If you haven't done your
3018  * homework with er_estimate_size() before calling this, you may be
3019  * in for a bruising.
3020  */
3021 
3022  /*
3023  * If there was trouble with the format for some reason, print out
3024  * something that seems a little reassuring.
3025  */
3026  if (fmt == NULL || fmt->fmt == NULL)
3027  {
3028  strncpy (er_entry_p->msg_area, er_Cached_msg[ER_ER_SUBSTITUTE_MSG], er_entry_p->msg_area_size);
3029  return ER_FAILED;
3030  }
3031 
3032  memcpy (&args, ap, sizeof (args));
3033 
3034  /*
3035  * Make room for the args that we are about to print. These have to
3036  * be snatched from the va_list in the proper order and stored in an
3037  * array so that we can have random access to them in order to
3038  * support the %<num>$<code> notation in the message, that is, when
3039  * we're printing the format, we may not have the luxury of printing
3040  * the arguments in the same order that they appear in the va_list.
3041  */
3042  if (er_entry_p->nargs < fmt->nspecs)
3043  {
3044  int size;
3045 
3046  er_entry_p->clear_args ();
3047 
3048  size = fmt->nspecs * sizeof (er_va_arg);
3049  er_entry_p->args = (er_va_arg *) ER_MALLOC (size);
3050  if (er_entry_p->args == NULL)
3051  {
3052  return ER_FAILED;
3053  }
3054  er_entry_p->nargs = fmt->nspecs;
3055  }
3056 
3057  /*
3058  * Now grab the args and put them in er_msg->args. The work that we
3059  * did earlier in er_study_fmt() tells us what the base type of each
3060  * va_list item is, and we use that info here.
3061  */
3062  for (i = 0; i < fmt->nspecs; i++)
3063  {
3064  switch (fmt->spec[i].code)
3065  {
3066  case 'i':
3067  er_entry_p->args[i].int_value = va_arg (args, int);
3068  break;
3069  case SPEC_CODE_LONGLONG:
3070  er_entry_p->args[i].longlong_value = va_arg (args, long long);
3071  break;
3072  case SPEC_CODE_SIZE_T:
3073  if (sizeof (size_t) == sizeof (long long int))
3074  {
3075  er_entry_p->args[i].longlong_value = va_arg (args, long long);
3076  }
3077  else
3078  {
3079  er_entry_p->args[i].int_value = va_arg (args, int);
3080  }
3081  break;
3082  case 'p':
3083  er_entry_p->args[i].pointer_value = va_arg (args, void *);
3084  break;
3085  case 'f':
3086  er_entry_p->args[i].double_value = va_arg (args, double);
3087  break;
3088  case 'L':
3089  er_entry_p->args[i].longdouble_value = va_arg (args, long double);
3090  break;
3091  case 's':
3092  er_entry_p->args[i].string_value = va_arg (args, char *);
3093  if (er_entry_p->args[i].string_value == NULL)
3094  {
3095  er_entry_p->args[i].string_value = "(null)";
3096  }
3097  break;
3098  default:
3099  /*
3100  * There should be no way to get in here with any other code;
3101  * er_study_fmt() should have protected us from that. If
3102  * we're here, it's likely that memory has been corrupted.
3103  */
3104  er_emergency (__FILE__, __LINE__, er_Cached_msg[ER_LOG_UNKNOWN_CODE], fmt->spec[i].code);
3105  return ER_FAILED;
3106  }
3107  }
3108 
3109  /*
3110  * Now do the printing. Use sprintf to do the actual formatting,
3111  * this time using the simplified conversion specs we saved during
3112  * er_study_fmt(). This frees us from relying on sprintf (or
3113  * vsprintf) actually implementing the %<num>$<code> feature, which
3114  * is evidently unimplemented on some platforms (Sun ANSI C, at
3115  * least).
3116  */
3117 
3118  p = fmt->fmt;
3119  q = p;
3120  s = er_entry_p->msg_area;
3121  i = 0;
3122  while ((q = strchr (p, '%')))
3123  {
3124  /*
3125  * Copy the text between the last conversion spec and the next
3126  * and then advance the pointers.
3127  */
3128  strncpy (s, p, q - p);
3129  s += q - p;
3130  p = q;
3131  q += 1;
3132 
3133  if (q[0] == '%')
3134  {
3135  *s++ = '%';
3136  p = q + 2;
3137  i += 1;
3138  continue;
3139  }
3140 
3141  /*
3142  * See if we've got a position specifier; it will look like a
3143  * sequence of digits followed by a '$'. Anything else is
3144  * assumed to be part of a conversion spec. If there is no
3145  * explicit position specifier, we use the current loop index as
3146  * the position specifier.
3147  */
3148  n = 0;
3149  while (char_isdigit (*q))
3150  {
3151  n *= 10;
3152  n += (*q) - '0';
3153  q += 1;
3154  }
3155  n = (*q == '$' && n) ? (n - 1) : i;
3156 
3157  /*
3158  * Format the specified argument using the simplified
3159  * (non-positional) conversion spec that we produced earlier.
3160  */
3161  switch (fmt->spec[n].code)
3162  {
3163  case 'i':
3164  sprintf (s, fmt->spec[n].spec, er_entry_p->args[n].int_value);
3165  break;
3166  case SPEC_CODE_LONGLONG:
3167  sprintf (s, fmt->spec[n].spec, er_entry_p->args[n].longlong_value);
3168  break;
3169  case SPEC_CODE_SIZE_T:
3170  if (sizeof (size_t) == sizeof (long long int))
3171  {
3172  sprintf (s, fmt->spec[n].spec, er_entry_p->args[n].longlong_value);
3173  }
3174  else
3175  {
3176  sprintf (s, fmt->spec[n].spec, er_entry_p->args[n].int_value);
3177  }
3178  break;
3179  case 'p':
3180  sprintf (s, fmt->spec[n].spec, er_entry_p->args[n].pointer_value);
3181  break;
3182  case 'f':
3183  sprintf (s, fmt->spec[n].spec, er_entry_p->args[n].double_value);
3184  break;
3185  case 'L':
3186  sprintf (s, fmt->spec[n].spec, er_entry_p->args[n].longdouble_value);
3187  break;
3188  case 's':
3189  sprintf (s, fmt->spec[n].spec, er_entry_p->args[n].string_value);
3190  break;
3191  default:
3192  /*
3193  * Can't get here.
3194  */
3195  break;
3196  }
3197 
3198  /*
3199  * Advance the pointers. The conversion spec has to end with one
3200  * of the characters in the strcspn() argument, and none of
3201  * those characters can appear before the end of the spec.
3202  */
3203  s += strlen (s);
3204  p += strcspn (p, "cdiopsuxXefgEFG") + 1;
3205  i += 1;
3206  }
3207 
3208  /*
3209  * And get the last part of the fmt string after the last conversion
3210  * spec...
3211  */
3212  strcpy (s, p);
3213  s[strlen (p)] = '\0';
3214 
3215  return NO_ERROR;
3216 }
3217 
3218 static bool
3220 {
3221  switch (severity)
3222  {
3224  case ER_ERROR_SEVERITY:
3226  return true;
3227  case ER_WARNING_SEVERITY:
3229  return false;
3230  default:
3231  assert (false);
3232  return false;
3233  }
3234 }
3235 
3236 #if defined (CS_MODE)
3237 void
3238 er_set_ignore_uninit (bool ignore)
3239 {
3240  er_Ignore_uninit = ignore;
3241 }
3242 #endif
3243 
3244 /* *INDENT-OFF* */
3245 namespace cuberr
3246 {
3247  manager::manager (const char * msg_file, er_exit_ask exit_arg)
3248  {
3249  if (er_init (msg_file, exit_arg) != NO_ERROR)
3250  {
3251  assert_release (false);
3252  }
3253  }
3254 
3255  manager::~manager (void)
3256  {
3258  }
3259 } // namespace cuberr
3260 /* *INDENT-ON* */
char * envvar_logdir_file(char *path, size_t size, const char *filename)
static int er_Errid_not_initialized
int nspecs
#define ER_MALLOC(size)
#define PATH_CURRENT
Definition: porting.h:349
static const char * er_Builtin_msg[]
static void * er_malloc_helper(std::size_t size, const char *file, int line)
MSG_CATD msgcat_get_descriptor(int cat_id)
void er_setid(int err_id)
static ER_FMT * er_find_fmt(int err_id, int num_args)
ER_SPEC spec_buf[16]
ER_SPEC * spec
#define NO_ERROR
Definition: error_code.h:46
#define TRUE
Definition: broker_admin.c:49
void er_stack_push(void)
long long longlong_value
static FILE * er_Event_pipe
static FILE * er_Msglog_fh
static FILE * er_file_backup(FILE *fp, const char *path)
const char * prog_name
const char * string_value
#define MAX_LINE
static API_MUTEX mutex
Definition: api_util.c:72
#define ER_FAILED
Definition: error_code.h:47
const char * boot_db_name(void)
Definition: boot_sr.c:459
#define ER_EV_STARTED
Definition: error_code.h:802
static er_log_handler_t er_Handler
static void _er_log_debug_internal(const char *file_name, const int line_no, const char *fmt, va_list *ap)
static char er_Msglog_filename_buff[PATH_MAX]
static void er_log(int err_id)
#define assert_release(e)
Definition: error_manager.h:96
char * er_get_area_error(char *buffer, int *length)
er_message & get_current_error_level(void)
static ER_FMT * er_create_fmt_msg(ER_FMT *fmt, int err_id, const char *msg)
#define ER_EV_BROKEN_PIPE
Definition: error_code.h:803
char spec[10]
#define CAST_STRLEN
Definition: porting.h:470
static char er_Accesslog_filename_buff[PATH_MAX]
int er_errid(void)
static void er_init_fmt(ER_FMT *fmt)
er_exit_ask
Definition: error_manager.h:99
static context * er_Singleton_context_p
void er_restore_last_error(void)
static size_t er_estimate_size(ER_FMT *fmt, va_list *ap)
static FILE * er_Accesslog_fh
void er_clearid(void)
#define er_log_debug(...)
#define ER_EV_STOPPED
Definition: error_code.h:804
#define ER_BO_CLIENT_CONNECTED
Definition: error_code.h:1212
void pop_error_stack_and_destroy(void)
void _er_log_debug(const char *file_name, const int line_no, const char *fmt,...)
int er_init(const char *msglog_filename, int exit_ask)
static int er_vsprintf(er_message *er_entry_p, ER_FMT *fmt, va_list *ap)
static bool er_Hasalready_initiated
static bool er_Event_started
void er_set_print_property(int print_console)
void register_thread_local(void)
const char * er_get_msglog_filename(void)
int fmt_length
void pop_error_stack(er_message &popped)
void er_stack_pop_and_keep_error(void)
int er_get_severity(void)
long double longdouble_value
void mht_destroy(MHT_TABLE *ht)
Definition: memory_hash.c:1140
int must_free
void er_set(int severity, const char *file_name, const int line_no, int err_id, int num_args,...)
#define ER_TP_INCOMPATIBLE_DOMAINS
Definition: error_code.h:248
#define assert(x)
void er_final(ER_FINAL_CODE do_global_final)
static bool er_is_error_severity(er_severity severity)
#define ASSERT_ALIGN(ptr, alignment)
#define ER_ACCESS_LOG_FILE_SUFFIX
#define ER_EV_CONNECT_HANDLER
Definition: error_code.h:798
std::mutex er_Message_cache_mutex
int prm_get_integer_value(PARAM_ID prm_id)
#define ER_GENERIC_ERROR
Definition: error_code.h:49
int er_set_area_error(char *server_area)
er_severity
static PTR_FNERLOG er_Fnlog[ER_MAX_SEVERITY+1]
void er_set_with_file(int severity, const char *file_name, const int line_no, int err_id, FILE *fp, int num_args,...)
#define ER_FK_CANT_ASSIGN_CACHE_ATTR
Definition: error_code.h:1159
void er_stack_clearall(void)
static er_message & get_thread_local_error(void)
static SIGNAL_HANDLER_FUNCTION saved_Sig_handler
static void er_clear_fmt(ER_FMT *fmt)
static const char * er_Accesslog_filename
#define MSGCAT_SET_INTERNAL
int cub_dirname_r(const char *path, char *pathbuf, size_t buflen)
Definition: porting.c:989
const char * rel_name(void)
static const char * er_unknown_severity
char * msgcat_gets(MSG_CATD msg_catd, int set_id, int msg_id, const char *s)
int char_isdigit(int c)
Definition: chartype.c:73
#define NULL
Definition: freelistheap.h:34
const char * er_msg(void)
pid_t pid
Definition: dynamic_load.c:955
#define MAX_INT_WIDTH
bool has_error_stack(void)
static char * er_Cached_msg[sizeof(er_Builtin_msg)/sizeof(const char *)]
#define ER_SEVERITY_STRING(severity)
char * er_get_ermsg_from_area_error(char *buffer)
MHT_TABLE * mht_create(const char *name, int est_size, unsigned int(*hash_func)(const void *key, unsigned int ht_size), int(*cmp_func)(const void *key1, const void *key2))
Definition: memory_hash.c:894
static int er_Fmt_msg_fail_count
#define ER_CT_REPRCNT_OVERFLOW
Definition: error_code.h:496
unsigned int mht_5strhash(const void *key, const unsigned int ht_size)
Definition: memory_hash.c:521
int msgcat_init(void)
char * db_name
static const char * er_severity_string[]
void er_set_with_oserror(int severity, const char *file_name, const int line_no, int err_id, int num_args,...)
#define MSGCAT_CATALOG_CUBRID
const bool & get_logging(void)
static ER_FMT er_Fmt_list[(-ER_LAST_ERROR)+1]
void er_stack_pop(void)
#define ER_FK_CANT_DROP_CACHE_ATTR
Definition: error_code.h:1161
static void er_event_final(void)
static void er_notify_event_on_error(int err_id)
#define ER_ERROR_WARNING_STRING(severity)
int err_id
#define MAX_DOUBLE_WIDTH
static void error(const char *msg)
Definition: gencat.c:331
void push_error_stack(void)
static bool er_Has_sticky_init
void deregister_thread_local(void)
#define ER_INTERRUPTED
Definition: error_code.h:51
static FILE * er_file_open(const char *path)
static bool er_Logfile_opened
#define MSGCAT_SET_ERROR
#define ARG_FILE_LINE
Definition: error_manager.h:44
bool er_has_error(void)
void er_print_callstack(const char *file_name, const int line_no, const char *fmt,...)
void reserve_message_area(std::size_t size)
er_log_handler_t er_register_log_handler(er_log_handler_t handler)
#define ER_MSG_LOG_FILE_SUFFIX
static void er_event_sigpipe_handler(int sig)
const char * file_name
#define OR_GET_INT(ptr)
int er_errid_if_has_error(void)
#define free_and_init(ptr)
Definition: memory_alloc.h:147
static bool er_Isa_null_device
#define strlen(s1)
Definition: intl_support.c:43
static int er_call_stack_init(void)
char * fmt
char * prm_get_string_value(PARAM_ID prm_id)
static int er_set_internal(int severity, const char *file_name, const int line_no, int err_id, int num_args, bool include_os_error, FILE *fp, va_list *ap_ptr)
static void er_internal_msg(ER_FMT *fmt, int code, int msg_num)
bool prm_get_bool_value(PARAM_ID prm_id)
#define INT_ALIGNMENT
Definition: memory_alloc.h:61
#define SPEC_CODE_LONGLONG
#define OR_PUT_INT(ptr, val)
void set_error(int error_id, int error_severity, const char *filename, int line_no)
void(* er_log_handler_t)(unsigned int)
void er_clear(void)
#define ER_SM_CATALOG_SPACE
Definition: error_code.h:367
static int er_fname_free(const void *key, void *data, void *args)
int i
Definition: dynamic_load.c:954
static int er_study_spec(const char *conversion_spec, char *simple_spec, int *position, int *width, int *va_class)
static void er_stop_on_error(void)
int mht_map(const MHT_TABLE *ht, int(*map_func)(const void *key, void *data, void *args), void *func_args)
Definition: memory_hash.c:2199
#define IS_ABS_PATH(p)
Definition: porting.h:357
char * msgcat_message(int cat_id, int set_id, int msg_id)
void(* PTR_FNERLOG)(int err_id)
static void er_event(void)
char * strdup(const char *str)
Definition: porting.c:901
static const char * er_Msglog_filename
void er_all(int *err_id, int *severity, int *n_levels, int *line_no, const char **file_name, const char **error_msg)
static void er_call_stack_final(void)
#define SPEC_CODE_SIZE_T
static void er_study_fmt(ER_FMT *fmt)
static unsigned int er_Eid
void er_stack_push_if_exists(void)
static int er_Print_to_console
static void er_emergency(const char *file, int line, const char *fmt,...)
#define CUB_MAXHOSTNAMELEN
Definition: porting.h:379
static jmp_buf er_Event_jmp_buf
#define TM_TRAN_INDEX()
void er_dump_call_stack(FILE *outfp)
Definition: stack_dump.c:500
static void er_call_stack_dump_on_error(int severity, int err_id)
static bool er_file_isa_null_device(const char *path)
std::mutex er_Log_file_mutex
#define ER_QPROC_OVERFLOW_COERCION
Definition: error_code.h:909
void(* SIGNAL_HANDLER_FUNCTION)(int sig_no)
Definition: porting.h:502
bool er_is_initialized(void)
int mht_compare_strings_are_equal(const void *key1, const void *key2)
Definition: memory_hash.c:767
#define ER_NUM_OVERFLOW
Definition: error_code.h:844
static int er_Exit_ask
bool sysprm_find_err_in_integer_list(PARAM_ID prm_id, int error_code)
#define ER_LAST_ERROR
Definition: error_code.h:1646
static bool er_Is_cached_msg
#define PATH_SEPARATOR
Definition: porting.h:347
int spec_top
SIGNAL_HANDLER_FUNCTION os_set_signal_handler(const int sig_no, SIGNAL_HANDLER_FUNCTION sig_handler)
Definition: porting.c:1333
const char ** p
Definition: dynamic_load.c:945
enum er_final_code ER_FINAL_CODE
#define ER_CT_UNKNOWN_CLASSID
Definition: error_code.h:491
er_msg_no
static int er_event_init(void)
std::size_t msg_area_size