From 748771379b9463ee7454630b3cb3f3087b3157cb Mon Sep 17 00:00:00 2001 From: Magnus Hagander <magnus@hagander.net> Date: Sat, 17 Oct 2009 00:24:51 +0000 Subject: [PATCH] Write to the Windows eventlog in UTF16, converting the message encoding as necessary. Itagaki Takahiro with some changes from me --- src/backend/utils/error/elog.c | 111 ++++++++++++++++---- src/backend/utils/mb/encnames.c | 175 +++++++++----------------------- src/backend/utils/mb/mbutils.c | 66 +++++++++++- src/include/mb/pg_wchar.h | 10 +- 4 files changed, 215 insertions(+), 147 deletions(-) diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index 06a78f2d701..5f30e783756 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -42,7 +42,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.217 2009/07/03 19:14:25 petere Exp $ + * $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.218 2009/10/17 00:24:50 mha Exp $ * *------------------------------------------------------------------------- */ @@ -111,8 +111,10 @@ static int syslog_facility = LOG_LOCAL0; static void write_syslog(int level, const char *line); #endif +static void write_console(const char *line, int len); + #ifdef WIN32 -static void write_eventlog(int level, const char *line); +static void write_eventlog(int level, const char *line, int len); #endif /* We provide a small stack of ErrorData records for re-entrant cases */ @@ -1567,10 +1569,11 @@ write_syslog(int level, const char *line) * Write a message line to the windows event log */ static void -write_eventlog(int level, const char *line) +write_eventlog(int level, const char *line, int len) { - int eventlevel = EVENTLOG_ERROR_TYPE; - static HANDLE evtHandle = INVALID_HANDLE_VALUE; + WCHAR *utf16; + int eventlevel = EVENTLOG_ERROR_TYPE; + static HANDLE evtHandle = INVALID_HANDLE_VALUE; if (evtHandle == INVALID_HANDLE_VALUE) { @@ -1606,8 +1609,34 @@ write_eventlog(int level, const char *line) break; } - - ReportEvent(evtHandle, + /* + * Convert message to UTF16 text and write it with ReportEventW, + * but fall-back into ReportEventA if conversion failed. + * + * Also verify that we are not on our way into error recursion trouble + * due to error messages thrown deep inside pgwin32_toUTF16(). + */ + if (GetDatabaseEncoding() != GetPlatformEncoding() && + !in_error_recursion_trouble()) + { + utf16 = pgwin32_toUTF16(line, len, NULL); + if (utf16) + { + ReportEventW(evtHandle, + eventlevel, + 0, + 0, /* All events are Id 0 */ + NULL, + 1, + 0, + (LPCWSTR *) &utf16, + NULL); + + pfree(utf16); + return; + } + } + ReportEventA(evtHandle, eventlevel, 0, 0, /* All events are Id 0 */ @@ -1619,6 +1648,52 @@ write_eventlog(int level, const char *line) } #endif /* WIN32 */ +static void +write_console(const char *line, int len) +{ +#ifdef WIN32 + /* + * WriteConsoleW() will fail of stdout is redirected, so just fall through + * to writing unconverted to the logfile in this case. + */ + if (GetDatabaseEncoding() != GetPlatformEncoding() && + !in_error_recursion_trouble() && + !redirection_done) + { + WCHAR *utf16; + int utf16len; + + utf16 = pgwin32_toUTF16(line, len, &utf16len); + if (utf16 != NULL) + { + HANDLE stdHandle; + DWORD written; + + stdHandle = GetStdHandle(STD_ERROR_HANDLE); + if (WriteConsoleW(stdHandle, utf16, utf16len, &written, NULL)) + { + pfree(utf16); + return; + } + + /* + * In case WriteConsoleW() failed, fall back to writing the message + * unconverted. + */ + pfree(utf16); + } + } +#else + /* + * Conversion on non-win32 platform is not implemented yet. + * It requires non-throw version of pg_do_encoding_conversion(), + * that converts unconvertable characters to '?' without errors. + */ +#endif + + write(fileno(stderr), line, len); +} + /* * setup formatted_log_time, for consistent times between CSV and regular logs */ @@ -2206,7 +2281,7 @@ send_message_to_server_log(ErrorData *edata) /* Write to eventlog, if enabled */ if (Log_destination & LOG_DESTINATION_EVENTLOG) { - write_eventlog(edata->elevel, buf.data); + write_eventlog(edata->elevel, buf.data, buf.len); } #endif /* WIN32 */ @@ -2230,10 +2305,10 @@ send_message_to_server_log(ErrorData *edata) * because that's really a pipe to the syslogger process. */ else if (pgwin32_is_service()) - write_eventlog(edata->elevel, buf.data); + write_eventlog(edata->elevel, buf.data, buf.len); #endif else - write(fileno(stderr), buf.data, buf.len); + write_console(buf.data, buf.len); } /* If in the syslogger process, try to write messages direct to file */ @@ -2256,12 +2331,12 @@ send_message_to_server_log(ErrorData *edata) { const char *msg = _("Not safe to send CSV data\n"); - write(fileno(stderr), msg, strlen(msg)); + write_console(msg, strlen(msg)); if (!(Log_destination & LOG_DESTINATION_STDERR) && whereToSendOutput != DestDebug) { /* write message to stderr unless we just sent it above */ - write(fileno(stderr), buf.data, buf.len); + write_console(buf.data, buf.len); } pfree(buf.data); } @@ -2642,6 +2717,9 @@ void write_stderr(const char *fmt,...) { va_list ap; +#ifdef WIN32 + char errbuf[2048]; /* Arbitrary size? */ +#endif fmt = _(fmt); @@ -2651,6 +2729,7 @@ write_stderr(const char *fmt,...) vfprintf(stderr, fmt, ap); fflush(stderr); #else + vsnprintf(errbuf, sizeof(errbuf), fmt, ap); /* * On Win32, we print to stderr if running on a console, or write to @@ -2658,16 +2737,12 @@ write_stderr(const char *fmt,...) */ if (pgwin32_is_service()) /* Running as a service */ { - char errbuf[2048]; /* Arbitrary size? */ - - vsnprintf(errbuf, sizeof(errbuf), fmt, ap); - - write_eventlog(ERROR, errbuf); + write_eventlog(ERROR, errbuf, strlen(errbuf)); } else { /* Not running as service, write to stderr */ - vfprintf(stderr, fmt, ap); + write_console(errbuf, strlen(errbuf)); fflush(stderr); } #endif diff --git a/src/backend/utils/mb/encnames.c b/src/backend/utils/mb/encnames.c index def8c84bad1..04df4cb5af6 100644 --- a/src/backend/utils/mb/encnames.c +++ b/src/backend/utils/mb/encnames.c @@ -2,7 +2,7 @@ * Encoding names and routines for work with it. All * in this file is shared bedween FE and BE. * - * $PostgreSQL: pgsql/src/backend/utils/mb/encnames.c,v 1.39 2009/04/24 08:43:50 mha Exp $ + * $PostgreSQL: pgsql/src/backend/utils/mb/encnames.c,v 1.40 2009/10/17 00:24:51 mha Exp $ */ #ifdef FRONTEND #include "postgres_fe.h" @@ -300,134 +300,55 @@ sizeof(pg_encname_tbl) / sizeof(pg_encname_tbl[0]) - 1; * XXX must be sorted by the same order as enum pg_enc (in mb/pg_wchar.h) * ---------- */ +#ifndef WIN32 +#define DEF_ENC2NAME(name, codepage) { #name, PG_##name } +#else +#define DEF_ENC2NAME(name, codepage) { #name, PG_##name, codepage } +#endif pg_enc2name pg_enc2name_tbl[] = { - { - "SQL_ASCII", PG_SQL_ASCII - }, - { - "EUC_JP", PG_EUC_JP - }, - { - "EUC_CN", PG_EUC_CN - }, - { - "EUC_KR", PG_EUC_KR - }, - { - "EUC_TW", PG_EUC_TW - }, - { - "EUC_JIS_2004", PG_EUC_JIS_2004 - }, - { - "UTF8", PG_UTF8 - }, - { - "MULE_INTERNAL", PG_MULE_INTERNAL - }, - { - "LATIN1", PG_LATIN1 - }, - { - "LATIN2", PG_LATIN2 - }, - { - "LATIN3", PG_LATIN3 - }, - { - "LATIN4", PG_LATIN4 - }, - { - "LATIN5", PG_LATIN5 - }, - { - "LATIN6", PG_LATIN6 - }, - { - "LATIN7", PG_LATIN7 - }, - { - "LATIN8", PG_LATIN8 - }, - { - "LATIN9", PG_LATIN9 - }, - { - "LATIN10", PG_LATIN10 - }, - { - "WIN1256", PG_WIN1256 - }, - { - "WIN1258", PG_WIN1258 - }, - { - "WIN866", PG_WIN866 - }, - { - "WIN874", PG_WIN874 - }, - { - "KOI8R", PG_KOI8R - }, - { - "WIN1251", PG_WIN1251 - }, - { - "WIN1252", PG_WIN1252 - }, - { - "ISO_8859_5", PG_ISO_8859_5 - }, - { - "ISO_8859_6", PG_ISO_8859_6 - }, - { - "ISO_8859_7", PG_ISO_8859_7 - }, - { - "ISO_8859_8", PG_ISO_8859_8 - }, - { - "WIN1250", PG_WIN1250 - }, - { - "WIN1253", PG_WIN1253 - }, - { - "WIN1254", PG_WIN1254 - }, - { - "WIN1255", PG_WIN1255 - }, - { - "WIN1257", PG_WIN1257 - }, - { - "KOI8U", PG_KOI8U - }, - { - "SJIS", PG_SJIS - }, - { - "BIG5", PG_BIG5 - }, - { - "GBK", PG_GBK - }, - { - "UHC", PG_UHC - }, - { - "GB18030", PG_GB18030 - }, - { - "JOHAB", PG_JOHAB - }, - { - "SHIFT_JIS_2004", PG_SHIFT_JIS_2004 - } + DEF_ENC2NAME(SQL_ASCII, 0), + DEF_ENC2NAME(EUC_JP, 20932), + DEF_ENC2NAME(EUC_CN, 20936), + DEF_ENC2NAME(EUC_KR, 51949), + DEF_ENC2NAME(EUC_TW, 0), + DEF_ENC2NAME(EUC_JIS_2004, 20932), + DEF_ENC2NAME(UTF8, 65001), + DEF_ENC2NAME(MULE_INTERNAL, 0), + DEF_ENC2NAME(LATIN1, 28591), + DEF_ENC2NAME(LATIN2, 28592), + DEF_ENC2NAME(LATIN3, 28593), + DEF_ENC2NAME(LATIN4, 28594), + DEF_ENC2NAME(LATIN5, 28599), + DEF_ENC2NAME(LATIN6, 0), + DEF_ENC2NAME(LATIN7, 0), + DEF_ENC2NAME(LATIN8, 0), + DEF_ENC2NAME(LATIN9, 28605), + DEF_ENC2NAME(LATIN10, 0), + DEF_ENC2NAME(WIN1256, 1256), + DEF_ENC2NAME(WIN1258, 1258), + DEF_ENC2NAME(WIN866, 866), + DEF_ENC2NAME(WIN874, 874), + DEF_ENC2NAME(KOI8R, 20866), + DEF_ENC2NAME(WIN1251, 1251), + DEF_ENC2NAME(WIN1252, 1252), + DEF_ENC2NAME(ISO_8859_5, 28595), + DEF_ENC2NAME(ISO_8859_6, 28596), + DEF_ENC2NAME(ISO_8859_7, 28597), + DEF_ENC2NAME(ISO_8859_8, 28598), + DEF_ENC2NAME(WIN1250, 1250), + DEF_ENC2NAME(WIN1253, 1253), + DEF_ENC2NAME(WIN1254, 1254), + DEF_ENC2NAME(WIN1255, 1255), + DEF_ENC2NAME(WIN1257, 1257), + DEF_ENC2NAME(KOI8U, 21866), + DEF_ENC2NAME(SJIS, 932), + DEF_ENC2NAME(BIG5, 950), + DEF_ENC2NAME(GBK, 936), + DEF_ENC2NAME(UHC, 0), + DEF_ENC2NAME(GB18030, 54936), + DEF_ENC2NAME(JOHAB, 0), + DEF_ENC2NAME(SHIFT_JIS_2004, 932) }; /* ---------- diff --git a/src/backend/utils/mb/mbutils.c b/src/backend/utils/mb/mbutils.c index cd3ebb219e2..c6e32ef6643 100644 --- a/src/backend/utils/mb/mbutils.c +++ b/src/backend/utils/mb/mbutils.c @@ -4,7 +4,7 @@ * * Tatsuo Ishii * - * $PostgreSQL: pgsql/src/backend/utils/mb/mbutils.c,v 1.89 2009/07/07 19:28:56 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/mb/mbutils.c,v 1.90 2009/10/17 00:24:51 mha Exp $ */ #include "postgres.h" @@ -58,6 +58,7 @@ static FmgrInfo *ToClientConvProc = NULL; */ static pg_enc2name *ClientEncoding = &pg_enc2name_tbl[PG_SQL_ASCII]; static pg_enc2name *DatabaseEncoding = &pg_enc2name_tbl[PG_SQL_ASCII]; +static pg_enc2name *PlatformEncoding = NULL; /* * During backend startup we can't set client encoding because we (a) @@ -978,3 +979,66 @@ pg_client_encoding(PG_FUNCTION_ARGS) Assert(ClientEncoding); return DirectFunctionCall1(namein, CStringGetDatum(ClientEncoding->name)); } + +int +GetPlatformEncoding(void) +{ + if (PlatformEncoding == NULL) + PlatformEncoding = &pg_enc2name_tbl[pg_get_encoding_from_locale("")]; + return PlatformEncoding->encoding; +} + +#ifdef WIN32 + +/* + * Result is palloc'ed null-terminated utf16 string. The character length + * is also passed to utf16len if not null. Returns NULL iff failed. + */ +WCHAR * +pgwin32_toUTF16(const char *str, int len, int *utf16len) +{ + WCHAR *utf16; + int dstlen; + UINT codepage; + + codepage = pg_enc2name_tbl[GetDatabaseEncoding()].codepage; + + /* + * Use MultiByteToWideChar directly if there is a corresponding codepage, + * or double conversion through UTF8 if not. + */ + if (codepage != 0) + { + utf16 = (WCHAR *) palloc(sizeof(WCHAR) * (len + 1)); + dstlen = MultiByteToWideChar(codepage, 0, str, len, utf16, len); + utf16[dstlen] = L'\0'; + } + else + { + char *utf8; + + utf8 = (char *) pg_do_encoding_conversion((unsigned char *) str, + len, GetDatabaseEncoding(), PG_UTF8); + if (utf8 != str) + len = strlen(utf8); + + utf16 = (WCHAR *) palloc(sizeof(WCHAR) * (len + 1)); + dstlen = MultiByteToWideChar(CP_UTF8, 0, utf8, len, utf16, len); + utf16[dstlen] = L'\0'; + + if (utf8 != str) + pfree(utf8); + } + + if (dstlen == 0 && len > 0) + { + pfree(utf16); + return NULL; /* error */ + } + + if (utf16len) + *utf16len = len; + return utf16; +} + +#endif diff --git a/src/include/mb/pg_wchar.h b/src/include/mb/pg_wchar.h index e9faff00130..353d612f758 100644 --- a/src/include/mb/pg_wchar.h +++ b/src/include/mb/pg_wchar.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/mb/pg_wchar.h,v 1.91 2009/06/11 14:49:11 momjian Exp $ + * $PostgreSQL: pgsql/src/include/mb/pg_wchar.h,v 1.92 2009/10/17 00:24:51 mha Exp $ * * NOTES * This is used both by the backend and by libpq, but should not be @@ -257,6 +257,9 @@ typedef struct pg_enc2name { char *name; pg_enc encoding; +#ifdef WIN32 + unsigned codepage; /* codepage for WIN32 */ +#endif } pg_enc2name; extern pg_enc2name pg_enc2name_tbl[]; @@ -402,6 +405,7 @@ extern const char *pg_get_client_encoding_name(void); extern void SetDatabaseEncoding(int encoding); extern int GetDatabaseEncoding(void); extern const char *GetDatabaseEncodingName(void); +extern int GetPlatformEncoding(void); extern void pg_bind_textdomain_codeset(const char *domainname); extern int pg_valid_client_encoding(const char *name); @@ -458,4 +462,8 @@ extern void mic2latin_with_table(const unsigned char *mic, unsigned char *p, extern bool pg_utf8_islegal(const unsigned char *source, int length); +#ifdef WIN32 +extern WCHAR *pgwin32_toUTF16(const char *str, int len, int *utf16len); +#endif + #endif /* PG_WCHAR_H */ -- GitLab