Windows: Console log handler fixups.

Make sure that we always print log messages on Windows. External programs
or scripts (including our test suite) might need to see log messages
independent of our console settings.

Make sure that we always use our log handler and that its stdout /
stderr routing matches GLib's. Flush our log output, which is something
that GLib's default handler sometimes doesn't do:
https://bugzilla.gnome.org/show_bug.cgi?id=792432

Bug: 15605
Change-Id: I4b17f2cb9269b2c87c21835d82770dae93bbfa20
Reviewed-on: https://code.wireshark.org/review/32412
Petri-Dish: Gerald Combs <gerald@wireshark.org>
Tested-by: Petri Dish Buildbot
Reviewed-by: Gerald Combs <gerald@wireshark.org>
This commit is contained in:
Gerald Combs 2019-03-14 10:55:28 -07:00
parent 3b2204e127
commit 14796eb04a
3 changed files with 109 additions and 139 deletions

View File

@ -27,7 +27,7 @@ console_log_handler(const char *log_domain, GLogLevelFlags log_level,
time_t curr;
struct tm *today;
const char *level;
FILE *stream = stderr;
/* ignore log message, if log_level isn't interesting based
upon the console log preferences.
@ -47,70 +47,57 @@ console_log_handler(const char *log_domain, GLogLevelFlags log_level,
/* the user wants a console or the application will terminate immediately */
create_console();
}
if (get_has_console()) {
/* For some unknown reason, the above doesn't appear to actually cause
anything to be sent to the standard output, so we'll just splat the
message out directly, just to make sure it gets out. */
#endif
switch(log_level & G_LOG_LEVEL_MASK) {
case G_LOG_LEVEL_ERROR:
level = "Err ";
break;
case G_LOG_LEVEL_CRITICAL:
level = "Crit";
break;
case G_LOG_LEVEL_WARNING:
level = "Warn";
break;
case G_LOG_LEVEL_MESSAGE:
level = "Msg ";
break;
case G_LOG_LEVEL_INFO:
level = "Info";
break;
case G_LOG_LEVEL_DEBUG:
level = "Dbg ";
break;
default:
fprintf(stderr, "unknown log_level %d\n", log_level);
level = NULL;
g_assert_not_reached();
}
/* create a "timestamp" */
time(&curr);
today = localtime(&curr);
guint64 microseconds = create_timestamp();
if (today != NULL) {
fprintf(stderr, "%02d:%02d:%02d.%03" G_GUINT64_FORMAT " %8s %s %s\n",
today->tm_hour, today->tm_min, today->tm_sec,
microseconds % 1000000 / 1000,
log_domain != NULL ? log_domain : "",
level, message);
} else {
fprintf(stderr, "Time not representable %8s %s %s\n",
log_domain != NULL ? log_domain : "",
level, message);
}
#ifdef _WIN32
if(log_level & G_LOG_LEVEL_ERROR) {
/* wait for a key press before the following error handler will terminate the program
this way the user at least can read the error message */
printf("\n\nPress any key to exit\n");
_getch();
}
switch(log_level & G_LOG_LEVEL_MASK) {
case G_LOG_LEVEL_ERROR:
level = "Err ";
break;
case G_LOG_LEVEL_CRITICAL:
level = "Crit";
break;
case G_LOG_LEVEL_WARNING:
level = "Warn";
break;
case G_LOG_LEVEL_MESSAGE:
level = "Msg ";
break;
case G_LOG_LEVEL_INFO:
level = "Info";
stream = stdout;
break;
case G_LOG_LEVEL_DEBUG:
level = "Dbg ";
stream = stdout;
break;
default:
fprintf(stderr, "unknown log_level %d\n", log_level);
level = NULL;
g_assert_not_reached();
}
/* create a "timestamp" */
time(&curr);
today = localtime(&curr);
guint64 microseconds = create_timestamp();
if (today != NULL) {
fprintf(stream, "%02d:%02d:%02d.%03" G_GUINT64_FORMAT " %8s %s %s\n",
today->tm_hour, today->tm_min, today->tm_sec,
microseconds % 1000000 / 1000,
log_domain != NULL ? log_domain : "",
level, message);
} else {
/* XXX - on UN*X, should we just use g_log_default_handler()?
We want the error messages to go to the standard output;
on macOS, that will cause them to show up in various
per-user logs accessible through Console (details depend
on whether you're running 10.0 through 10.4 or running
10.5 and later), and, on other UN*X desktop environments,
if they don't show up in some form of console log, that's
a deficiency in that desktop environment. (Too bad
Windows doesn't set the standard output and error for
GUI apps to something that shows up in such a log.) */
g_log_default_handler(log_domain, log_level, message, user_data);
fprintf(stream, "Time not representable %8s %s %s\n",
log_domain != NULL ? log_domain : "",
level, message);
}
fflush(stream);
#ifdef _WIN32
if(log_level & G_LOG_LEVEL_ERROR) {
/* wait for a key press before the following error handler will terminate the program
this way the user at least can read the error message */
printf("\n\nPress any key to exit\n");
_getch();
}
#endif
}

View File

@ -121,44 +121,47 @@ create_console(void)
return;
}
if (!has_console) {
/* Are the standard input, output, and error invalid handles? */
must_redirect_stdin = needs_redirection(STD_INPUT_HANDLE);
must_redirect_stdout = needs_redirection(STD_OUTPUT_HANDLE);
must_redirect_stderr = needs_redirection(STD_ERROR_HANDLE);
if (has_console) {
return;
}
/* If none of them are invalid, we don't need to do anything. */
if (!must_redirect_stdin && !must_redirect_stdout && !must_redirect_stderr)
return;
/* Are the standard input, output, and error invalid handles? */
must_redirect_stdin = needs_redirection(STD_INPUT_HANDLE);
must_redirect_stdout = needs_redirection(STD_OUTPUT_HANDLE);
must_redirect_stderr = needs_redirection(STD_ERROR_HANDLE);
/* OK, at least one of them needs to be redirected to a console;
try to attach to the parent process's console and, if that fails,
try to create one. */
/*
* See if we have an existing console (i.e. we were run from a
* command prompt).
*/
if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
/* Probably not, as we couldn't attach to the parent process's
console.
Try to create a console.
/* If none of them are invalid, we don't need to do anything. */
if (!must_redirect_stdin && !must_redirect_stdout && !must_redirect_stderr)
return;
According to a comment on
/* OK, at least one of them needs to be redirected to a console;
try to attach to the parent process's console and, if that fails,
try to create one. */
/*
* See if we have an existing console (i.e. we were run from a
* command prompt).
*/
if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
/* Probably not, as we couldn't attach to the parent process's
console.
Try to create a console.
According to a comment on
http://msdn.microsoft.com/en-us/library/windows/desktop/ms681952(v=vs.85).aspx
(which now redirects to a docs.microsoft.com page that is
devoid of comments, and which is not available on the
Wayback Machine)
(which now redirects to a docs.microsoft.com page that is
devoid of comments, and which is not available on the
Wayback Machine)
and according to
and according to
http://connect.microsoft.com/VisualStudio/feedback/details/689696/installing-security-update-kb2507938-prevents-console-allocation
(which has disappeared, and isn't available on the Wayback
Machine)
(which has disappeared, and isn't available on the Wayback
Machine)
and
and
https://answers.microsoft.com/en-us/windows/forum/windows_xp-windows_update/kb2567680-andor-kb2507938-breaks-attachconsole-api/e8191280-2d49-4be4-9918-18486fba0afa
@ -166,39 +169,38 @@ even a failed attempt to attach to another process's console
will cause subsequent AllocConsole() calls to fail, possibly due
to bugs introduced by a security patch. To work around this, we
do a FreeConsole() first. */
FreeConsole();
if (AllocConsole()) {
/* That succeeded. */
console_wait = TRUE;
SetConsoleTitle(_T("Wireshark Debug Console"));
} else {
/* On Windows XP, this still fails; FreeConsole() apparently
doesn't clear the state, as it does on Windows 7. */
return; /* couldn't create console */
}
FreeConsole();
if (AllocConsole()) {
/* That succeeded. */
console_wait = TRUE;
SetConsoleTitle(_T("Wireshark Debug Console"));
} else {
/* On Windows XP, this still fails; FreeConsole() apparently
doesn't clear the state, as it does on Windows 7. */
return; /* couldn't create console */
}
if (must_redirect_stdin)
ws_freopen("CONIN$", "r", stdin);
if (must_redirect_stdout) {
ws_freopen("CONOUT$", "w", stdout);
fprintf(stdout, "\n");
}
if (must_redirect_stderr) {
ws_freopen("CONOUT$", "w", stderr);
fprintf(stderr, "\n");
}
/* Now register "destroy_console()" as a routine to be called just
before the application exits, so that we can destroy the console
after the user has typed a key (so that the console doesn't just
disappear out from under them, giving the user no chance to see
the message(s) we put in there). */
atexit(destroy_console);
/* Well, we have a console now. */
has_console = TRUE;
}
if (must_redirect_stdin)
ws_freopen("CONIN$", "r", stdin);
if (must_redirect_stdout) {
ws_freopen("CONOUT$", "w", stdout);
fprintf(stdout, "\n");
}
if (must_redirect_stderr) {
ws_freopen("CONOUT$", "w", stderr);
fprintf(stderr, "\n");
}
/* Now register "destroy_console()" as a routine to be called just
before the application exits, so that we can destroy the console
after the user has typed a key (so that the console doesn't just
disappear out from under them, giving the user no chance to see
the message(s) we put in there). */
atexit(destroy_console);
/* Well, we have a console now. */
has_console = TRUE;
}
void
@ -223,18 +225,6 @@ get_console_wait(void)
return console_wait;
}
void
set_has_console(gboolean set_has_console)
{
has_console = set_has_console;
}
gboolean
get_has_console(void)
{
return has_console;
}
void
set_stdin_capture(gboolean set_stdin_capture)
{

View File

@ -40,13 +40,6 @@ void set_console_wait(gboolean console_wait);
*/
gboolean get_console_wait(void);
/** Set has console. GTK+ only.
* @param has_console set/no set has_console
*/
void set_has_console(gboolean has_console);
gboolean get_has_console(void);
/** Set stdin capture.
* @param console_wait set/no stdin_capture
*/