28
28
* before including glib.h.
31
#include "vmware/tools/utils.h"
31
#include "vmtoolsInt.h"
33
33
#include <stdlib.h>
34
34
#include <glib/gstdio.h>
35
35
#if defined(G_PLATFORM_WIN32)
38
38
# include <unistd.h>
39
# include <sys/resource.h>
40
# include <sys/time.h>
42
43
#if defined(G_PLATFORM_WIN32)
43
44
# include "coreDump.h"
47
46
#include "system.h"
49
#if defined(G_PLATFORM_WIN32)
50
# define DEFAULT_HANDLER VMToolsLogOutputDebugString
52
# define DEFAULT_HANDLER VMToolsLogFile
55
#define LOGGING_GROUP "logging"
56
48
#define MAX_DOMAIN_LEN 64
50
/** Alias to retrieve the default handler from the handler array. */
51
#define DEFAULT_HANDLER (&gHandlers[ARRAYSIZE(gHandlers) - 1])
58
53
/** Tells whether the given log level is a fatal error. */
59
54
#define IS_FATAL(level) ((level) & G_LOG_FLAG_FATAL)
69
64
/** Clean up the contents of a log handler. */
70
65
#define CLEAR_LOG_HANDLER(handler) do { \
71
if ((handler)->file != NULL) { \
72
fclose((handler)->file); \
66
if ((handler) != NULL) { \
67
g_free((handler)->domain); \
68
(handler)->dtor(handler); \
74
g_free((handler)->path); \
75
g_free((handler)->domain); \
80
VMToolsLogFile(const gchar *domain,
85
73
#if defined(G_PLATFORM_WIN32)
87
75
VMToolsLogOutputDebugString(const gchar *domain,
93
typedef struct LogHandlerData {
81
typedef LogHandlerData * (*LogHandlerConfigFn)(const gchar *domain,
85
typedef struct LogHandler {
88
LogHandlerConfigFn configfn;
93
* List of available log handlers, mapped to their config file entries.
94
* The NULL entry means the default handler (if the config file entry
95
* doesn't exist, or doesn't match any existing handler), and must be
98
static LogHandler gHandlers[] = {
99
{ 0, "std", VMStdLoggerConfig },
100
{ 1, "file", VMFileLoggerConfig },
101
{ 2, "file+", VMFileLoggerConfig },
103
{ 3, "outputdebugstring", VMDebugOutputConfig },
104
{ -1, NULL, VMDebugOutputConfig },
106
{ -1, NULL, VMStdLoggerConfig },
104
111
static gchar *gLogDomain = NULL;
105
112
static gboolean gEnableCoreDump = TRUE;
106
113
static gboolean gLogEnabled = FALSE;
107
114
static guint gPanicCount = 0;
108
115
static LogHandlerData *gDefaultData = NULL;
109
static GLogFunc gDefaultLogFunc = DEFAULT_HANDLER;
116
static LogHandlerData *gErrorData = NULL;
110
117
static GPtrArray *gDomains = NULL;
112
119
/* Internal functions. */
140
* Opens a log file for writing, backing up the existing log file if one is
141
* present. Only one old log file is preserved.
143
* @param[in] path Path to log file.
144
* @param[in] append Whether to open the log for appending (if TRUE, a backup
145
* file is not generated).
147
* @return File pointer for writing to the file (NULL on error).
151
VMToolsLogOpenFile(const gchar *path,
154
FILE *logfile = NULL;
157
ASSERT(path != NULL);
158
pathLocal = VMTOOLS_GET_FILENAME_LOCAL(path, NULL);
160
if (!append && g_file_test(pathLocal, G_FILE_TEST_EXISTS)) {
161
/* Back up existing log file. */
162
gchar *bakFile = g_strdup_printf("%s.old", pathLocal);
163
if (!g_file_test(bakFile, G_FILE_TEST_IS_DIR) &&
164
(!g_file_test(bakFile, G_FILE_TEST_EXISTS) ||
165
g_unlink(bakFile) == 0)) {
166
g_rename(pathLocal, bakFile);
173
logfile = g_fopen(pathLocal, append ? "a" : "w");
174
VMTOOLS_RELEASE_FILENAME_LOCAL(pathLocal);
180
147
* Creates a formatted message to be logged. The format of the message will be:
182
149
* [timestamp] [domain] [level] Log message
184
151
* @param[in] message User log message.
185
152
* @param[in] domain Log domain.
186
153
* @param[in] level Log level.
187
* @param[in] timestamp Whether to print the timestamp.
188
* @param[in] printAppName Whether to include the app name (a.k.a. the default
189
* domain) in the message.
154
* @param[in] data Log handler data.
191
* @return Formatted log message in the current encoding. Should be free()'d.
156
* @return Formatted log message according to the log domain's config.
157
* Should be g_free()'d.
195
161
VMToolsLogFormat(const gchar *message,
196
162
const gchar *domain,
197
163
GLogLevelFlags level,
199
gboolean printAppName)
164
LogHandlerData *data)
201
166
char *msg = NULL;
202
char *msgCurr = NULL;
203
167
const char *slevel;
280
244
if (msgCurr != NULL && msgCurr[msgCurrLen - 2] == '\n') {
281
245
msgCurr[msgCurrLen - 1] = '\0';
285
if (msgCurr != NULL) {
257
* Logs a message to the error log domain. This is used when a log handler
258
* encounters an error while logging a message, and wants to log information
261
* @param[in] domain Log domain.
262
* @param[in] level Log level.
263
* @param[in] fmt Message format.
264
* @param[in] ... Message parameters.
268
VMToolsError(const gchar *domain,
269
GLogLevelFlags level,
278
g_vasprintf(&message, fmt, args);
281
formatted = VMToolsLogFormat(message, domain, level, gErrorData);
283
gErrorData->logfn(domain, level, formatted, gErrorData, VMToolsError);
294
290
* Aborts the program, optionally creating a core dump.
297
293
static INLINE NORETURN void
298
294
VMToolsLogPanic(void)
300
297
if (gEnableCoreDump) {
301
#if defined(G_PLATFORM_WIN32)
302
299
CoreDump_CoreDump();
302
if (getcwd(cwd, sizeof cwd) != NULL) {
303
if (access(cwd, W_OK) == -1) {
305
* Can't write to the working dir. chdir() to the user's home
306
* directory as an attempt to get a valid core dump.
308
const char *home = getenv("HOME");
311
/* Just to make glibc headers happy. */
312
#if defined(G_PLATFORM_WIN32)
314
* Logs a message to OutputDebugString.
316
* @param[in] domain Log domain.
317
* @param[in] level Log level.
318
* @param[in] message Message to log.
319
* @param[in] _data LogHandlerData pointer.
323
VMToolsLogOutputDebugString(const gchar *domain,
324
GLogLevelFlags level,
325
const gchar *message,
328
LogHandlerData *data = _data;
329
if (SHOULD_LOG(level, data)) {
330
char *msg = VMToolsLogFormat(message, domain, level, FALSE, TRUE);
332
OutputDebugStringA(msg);
336
if (IS_FATAL(level)) {
344
* Logs a message to a file streams. When writing to the standard streams,
345
* any level >= MESSAGE will cause the message to go to stdout; otherwise,
346
* it will go to stderr.
348
* @param[in] domain Log domain.
349
* @param[in] level Log level.
350
* @param[in] message Message to log.
351
* @param[in] _data LogHandlerData pointer.
355
VMToolsLogFile(const gchar *domain,
356
GLogLevelFlags level,
357
const gchar *message,
360
LogHandlerData *data = _data;
361
if (SHOULD_LOG(level, data)) {
325
* Log handler function that does the common processing of log messages,
326
* and delegates the actual printing of the message to the given handler.
328
* @param[in] domain Log domain.
329
* @param[in] level Log level.
330
* @param[in] message Message to log.
331
* @param[in] _data LogHandlerData pointer.
335
VMToolsLog(const gchar *domain,
336
GLogLevelFlags level,
337
const gchar *message,
340
LogHandlerData *data = _data;
341
if (SHOULD_LOG(level, data)) {
342
gchar *msg = VMToolsLogFormat(message, domain, level, data);
362
343
data = data->inherited ? gDefaultData : data;
363
if (!data->error && data->file == NULL && data->path != NULL) {
364
data->file = VMToolsLogOpenFile(data->path, data->append);
365
if (data->file == NULL) {
367
* glib's documentation says that we can set up log handlers that
368
* handle G_LOG_FLAG_RECURSION, but looking at the source code of
369
* the g_logv() function that's not really true (at least up to
370
* current top of tree - glib 2.20?). So we have to avoid recursion
371
* here and bypass the log system.
374
g_snprintf(warn, sizeof warn,
375
"Unable to open log file %s for domain %s.\n",
376
data->path, data->domain);
379
DEFAULT_HANDLER(domain, G_LOG_LEVEL_WARNING | G_LOG_FLAG_RECURSION,
383
if (!(level & G_LOG_FLAG_RECURSION) && data->error) {
384
DEFAULT_HANDLER(domain, level | G_LOG_FLAG_RECURSION, message, gDefaultData);
386
gchar *msg = VMToolsLogFormat(message, domain, level, TRUE, FALSE);
388
FILE *dest = (data->file != NULL) ? data->file
389
: ((level < G_LOG_LEVEL_MESSAGE) ? stderr : stdout);
344
if (!data->logfn(domain, level, msg, data, VMToolsError)) {
346
* The logger for some reason indicated that it couldn't log the
347
* message. Use the error handler to do it, and ignore any
350
VMToolsError(domain, level | G_LOG_FLAG_RECURSION, message,
351
gErrorData, VMToolsError);
396
355
if (IS_FATAL(level)) {
397
356
VMToolsLogPanic();
450
410
g_snprintf(key, sizeof key, "%s.handler", domain);
451
411
handler = g_key_file_get_string(cfg, LOGGING_GROUP, key, NULL);
453
if (handler == NULL) {
454
if (strcmp(domain, gLogDomain) == 0) {
455
handlerFn = DEFAULT_HANDLER;
457
handlerFn = gDefaultLogFunc;
459
} else if (strcmp(handler, "std") == 0) {
460
handlerFn = VMToolsLogFile;
461
} else if (strcmp(handler, "file") == 0 ||
462
strcmp(handler, "file+") == 0) {
463
/* Don't set up the file sink if logging is disabled. */
464
if (strcmp(level, "none") != 0) {
465
handlerFn = VMToolsLogFile;
466
g_snprintf(key, sizeof key, "%s.data", domain);
467
logpath = g_key_file_get_string(cfg, LOGGING_GROUP, key, NULL);
468
if (logpath == NULL) {
469
g_warning("Missing log path for file handler (%s).\n", domain);
473
* Do some variable expansion in the input string. Currently only
474
* ${USER} and ${PID} are expanded.
482
vars[1] = Hostinfo_GetUser();
483
vars[3] = g_strdup_printf("%"FMTPID, getpid());
485
for (i = 0; i < ARRAYSIZE(vars); i += 2) {
486
char *last = logpath;
488
while ((start = strstr(last, vars[i])) != NULL) {
490
char *end = start + strlen(vars[i]);
491
size_t offset = (start - last) + strlen(vars[i+1]);
494
tmp = g_strdup_printf("%s%s%s", logpath, vars[i+1], end);
497
last = logpath + offset;
413
if (handler != NULL) {
414
for (i = 0; i < ARRAYSIZE(gHandlers) - 1; i++) {
415
if (handler == gHandlers[i].name ||
416
strcmp(handler, gHandlers[i].name) == 0) {
417
hid = gHandlers[i].id;
418
configfn = gHandlers[i].configfn;
505
#if defined(G_PLATFORM_WIN32)
506
} else if (strcmp(handler, "outputdebugstring") == 0) {
507
handlerFn = VMToolsLogOutputDebugString;
423
if (configfn == NULL) {
424
g_warning("Unknown log handler '%s', using default.", handler);
428
data = configfn(domain, handler, cfg);
429
} else if (strcmp(domain, gLogDomain) == 0) {
431
* If no handler defined and we're configuring the default domain,
432
* then instantiate the default handler.
434
hid = DEFAULT_HANDLER->id;
435
configfn = DEFAULT_HANDLER->configfn;
436
data = configfn(domain, NULL, cfg);
437
ASSERT(data != NULL);
510
g_warning("Unknown log handler: %s\n", handler);
439
/* An inherited handler. Just create a dummy instance. */
440
ASSERT(gDefaultData != NULL);
441
data = g_new0(LogHandlerData, 1);
442
data->inherited = TRUE;
443
data->logfn = gDefaultData->logfn;
444
data->convertToLocal = gDefaultData->convertToLocal;
445
data->timestamp = gDefaultData->timestamp;
446
data->shared = gDefaultData->shared;
447
data->dtor = (LogHandlerDestroyFn) g_free;
451
ASSERT(data->logfn != NULL);
452
ASSERT(data->dtor != NULL);
514
454
/* Parse the log level configuration, and build the mask. */
515
455
if (strcmp(level, "error") == 0) {
516
456
levelsMask = G_LOG_LEVEL_ERROR;
674
597
/* Try to find the matching old config. */
675
598
for (j = 0; j < oldDomains->len; j++) {
676
599
LogHandlerData *olddata = g_ptr_array_index(oldDomains, j);
677
if (strcmp(data->domain, olddata->domain) == 0) {
678
if (data->path != NULL && olddata->file != NULL) {
679
ASSERT(data->file == NULL);
680
data->file = olddata->file;
681
olddata->file = NULL;
600
if (data->type == olddata->type &&
601
strcmp(data->domain, olddata->domain) == 0) {
602
if (!data->inherited && data->copyfn != NULL) {
603
data->copyfn(data, olddata);
713
633
/* Public API. */
638
* Attaches a console to the current process. If the parent process already has
639
* a console open, reuse it. Otherwise, create a new console for the current
640
* process. Win32-only.
642
* It's safe to call this function multiple times (it won't do anything if
643
* the process already has a console).
645
* @note Attaching to the parent process's console is only available on XP and
648
* @return Whether the process is attached to a console.
652
VMTools_AttachConsole(void)
654
typedef BOOL (WINAPI *AttachConsoleFn)(DWORD);
655
gboolean ret = FALSE;
656
AttachConsoleFn _AttachConsole;
658
if (GetConsoleWindow() != NULL) {
662
_AttachConsole = (AttachConsoleFn) GetProcAddress(GetModuleHandleW(L"kernel32.dll"),
664
if ((_AttachConsole != NULL && _AttachConsole(ATTACH_PARENT_PROCESS)) ||
668
fptr = _wfreopen(L"CONOUT$", L"a", stdout);
670
g_warning("_wfreopen failed for stdout/CONOUT$: %d (%s)",
671
errno, strerror(errno));
675
fptr = _wfreopen(L"CONOUT$", L"a", stderr);
677
g_warning("_wfreopen failed for stderr/CONOUT$: %d (%s)",
678
errno, strerror(errno));
681
setvbuf(fptr, NULL, _IONBF, 0);
687
g_warning("Console redirection unavailable.");
716
695
* Configures the logging system according to the configuration in the given
757
741
gLogDomain = g_strdup(defaultDomain);
760
* If no logging config data exists, then we install a default log handler,
761
* just so we override the default glib one, since the caller has asked us to
762
* enable our logging system.
764
if (cfg == NULL || !g_key_file_has_group(cfg, LOGGING_GROUP)) {
765
gDefaultData = g_malloc0(sizeof *gDefaultData);
766
gDefaultData->domain = g_strdup(defaultDomain);
767
gDefaultData->mask = G_LOG_LEVEL_ERROR |
768
G_LOG_LEVEL_CRITICAL |
770
#if defined(VMX86_DEBUG)
771
gDefaultData->mask |= G_LOG_LEVEL_MESSAGE;
773
g_log_set_default_handler(gDefaultLogFunc, gDefaultData);
742
gErrorData = DEFAULT_HANDLER->configfn(gLogDomain, NULL, NULL);
778
745
* Configure the default domain first. See function documentation for
809
776
"enableCoreDump", NULL);
780
* If core dumps are enabled (default: TRUE), then set up the exception
781
* filter on Win32. On POSIX systems, try to modify the resource limit
782
* to allow core dumps, but don't complain if it fails. Core dumps may
783
* still fail, e.g., if the current directory is not writable by the
784
* user running the process.
786
* On POSIX systems, if the process is itself requesting a core dump (e.g.,
787
* by calling Panic() or g_error()), the core dump routine will try to find
788
* a location where it can successfully create the core file. Applications
789
* can try to set up core dump filters (e.g., a signal handler for SIGSEGV)
790
* that can then call any of the core dumping functions handled by this
793
* On POSIX systems, the maximum size of a core dump can be controlled by
794
* the "maxCoreSize" config option, where "0" means "no limit". By default,
797
if (gEnableCoreDump) {
799
CoreDump_SetUnhandledExceptionFilter();
802
struct rlimit limit = { 0, 0 };
804
getrlimit(RLIMIT_CORE, &limit);
805
if (limit.rlim_max != 0) {
806
limit.rlim_cur = (rlim_t) g_key_file_get_integer(cfg,
811
limit.rlim_cur = 5 * 1024 * 1024;
813
} else if (limit.rlim_cur == 0) {
814
limit.rlim_cur = RLIM_INFINITY;
817
limit.rlim_cur = MAX(limit.rlim_cur, limit.rlim_max);
818
if (setrlimit(RLIMIT_CORE, &limit) == -1) {
819
g_message("Failed to set core dump size limit, error %d (%s)\n",
820
errno, g_strerror(errno));
822
g_message("Core dump limit set to %d", (int) limit.rlim_cur);
813
828
/* If needed, restore the old configuration. */
815
830
VMToolsRestoreLogging(oldDefault, oldDomains);
817
831
if (oldDomains != NULL) {
818
832
g_ptr_array_free(oldDomains, TRUE);
822
836
gLogEnabled |= force;
839
g_key_file_free(cfg);
877
894
va_start(args, fmt);
878
if (gPanicCount == 1) {
895
if (gPanicCount == 0) {
879
896
g_logv(gLogDomain, G_LOG_LEVEL_ERROR, fmt, args);
898
* In case an user-installed custom handler doesn't panic on error, force a
899
* core dump. Also force a dump in the recursive case.
902
} else if (gPanicCount == 1) {
904
* Use a stack allocated string since we're in a recursive panic, so
905
* probably already in a weird state.
908
g_vsnprintf(msg, sizeof msg, fmt, args);
909
fprintf(stderr, "Recursive panic: %s\n", msg);
882
g_vasprintf(&msg, fmt, args);
883
if (gPanicCount == 2) {
884
fprintf(stderr, "Recursive panic: %s\n", msg);
886
fprintf(stderr, "Recursive panic, giving up: %s\n", msg);
912
fprintf(stderr, "Recursive panic, giving up.\n");
893
* In case an user-installed custom handler doesn't panic on error, force a
894
* core dump. Also force a dump in the recursive case.