~n-muench/ubuntu/oneiric/open-vm-tools/open-vm-tools.fix-836277

« back to all changes in this revision

Viewing changes to libvmtools/vmtoolsLog.c

  • Committer: Evan Broder
  • Date: 2010-03-21 23:26:53 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: broder@mit.edu-20100321232653-5a57r7v7ch4o6byv
Merging shared upstream rev into target branch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
 *    before including glib.h.
29
29
 */
30
30
 
31
 
#include "vmware/tools/utils.h"
 
31
#include "vmtoolsInt.h"
32
32
#include <stdio.h>
33
33
#include <stdlib.h>
34
34
#include <glib/gstdio.h>
35
35
#if defined(G_PLATFORM_WIN32)
36
 
#  include <process.h>
 
36
#  include <windows.h>
37
37
#else
38
38
#  include <unistd.h>
 
39
#  include <sys/resource.h>
 
40
#  include <sys/time.h>
39
41
#endif
40
42
 
41
 
#include "codeset.h"
42
43
#if defined(G_PLATFORM_WIN32)
43
44
#  include "coreDump.h"
44
45
#endif
45
 
#include "file.h"
46
 
#include "hostinfo.h"
47
46
#include "system.h"
48
47
 
49
 
#if defined(G_PLATFORM_WIN32)
50
 
#  define  DEFAULT_HANDLER    VMToolsLogOutputDebugString
51
 
#else
52
 
#  define  DEFAULT_HANDLER    VMToolsLogFile
53
 
#endif
54
 
 
55
 
#define LOGGING_GROUP         "logging"
56
48
#define MAX_DOMAIN_LEN        64
57
49
 
 
50
/** Alias to retrieve the default handler from the handler array. */
 
51
#define DEFAULT_HANDLER (&gHandlers[ARRAYSIZE(gHandlers) - 1])
 
52
 
58
53
/** Tells whether the given log level is a fatal error. */
59
54
#define IS_FATAL(level) ((level) & G_LOG_FLAG_FATAL)
60
55
 
68
63
 
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);         \
73
69
   }                                    \
74
 
   g_free((handler)->path);             \
75
 
   g_free((handler)->domain);           \
76
70
} while (0)
77
71
 
78
72
 
79
 
static void
80
 
VMToolsLogFile(const gchar *domain,
81
 
               GLogLevelFlags level,
82
 
               const gchar *message,
83
 
               gpointer _data);
84
 
 
85
73
#if defined(G_PLATFORM_WIN32)
86
74
static void
87
75
VMToolsLogOutputDebugString(const gchar *domain,
90
78
                            gpointer _data);
91
79
#endif
92
80
 
93
 
typedef struct LogHandlerData {
94
 
   gchar            *domain;
95
 
   GLogLevelFlags    mask;
96
 
   FILE             *file;
97
 
   gchar            *path;
98
 
   gboolean          append;
99
 
   guint             handlerId;
100
 
   gboolean          inherited;
101
 
   gboolean          error;
102
 
} LogHandlerData;
 
81
typedef LogHandlerData * (*LogHandlerConfigFn)(const gchar *domain,
 
82
                                               const gchar *name,
 
83
                                               GKeyFile *cfg);
 
84
 
 
85
typedef struct LogHandler {
 
86
   const guint          id;
 
87
   const gchar         *name;
 
88
   LogHandlerConfigFn   configfn;
 
89
} LogHandler;
 
90
 
 
91
 
 
92
/**
 
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
 
96
 * the last entry.
 
97
 */
 
98
static LogHandler gHandlers[] = {
 
99
   { 0,  "std",               VMStdLoggerConfig },
 
100
   { 1,  "file",              VMFileLoggerConfig },
 
101
   { 2,  "file+",             VMFileLoggerConfig },
 
102
#if defined(_WIN32)
 
103
   { 3,  "outputdebugstring", VMDebugOutputConfig },
 
104
   { -1, NULL,                VMDebugOutputConfig },
 
105
#else
 
106
   { -1, NULL,                VMStdLoggerConfig },
 
107
#endif
 
108
};
 
109
 
103
110
 
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;
111
118
 
112
119
/* Internal functions. */
137
144
 
138
145
 
139
146
/**
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.
142
 
 *
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).
146
 
 *
147
 
 * @return File pointer for writing to the file (NULL on error).
148
 
 */
149
 
 
150
 
static FILE *
151
 
VMToolsLogOpenFile(const gchar *path,
152
 
                   gboolean append)
153
 
{
154
 
   FILE *logfile = NULL;
155
 
   gchar *pathLocal;
156
 
 
157
 
   ASSERT(path != NULL);
158
 
   pathLocal = VMTOOLS_GET_FILENAME_LOCAL(path, NULL);
159
 
 
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);
167
 
      } else {
168
 
         g_unlink(pathLocal);
169
 
      }
170
 
      g_free(bakFile);
171
 
   }
172
 
 
173
 
   logfile = g_fopen(pathLocal, append ? "a" : "w");
174
 
   VMTOOLS_RELEASE_FILENAME_LOCAL(pathLocal);
175
 
   return logfile;
176
 
}
177
 
 
178
 
 
179
 
/**
180
147
 * Creates a formatted message to be logged. The format of the message will be:
181
148
 *
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.
190
155
 *
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.
192
158
 */
193
159
 
194
 
static char *
 
160
static gchar *
195
161
VMToolsLogFormat(const gchar *message,
196
162
                 const gchar *domain,
197
163
                 GLogLevelFlags level,
198
 
                 gboolean timestamp,
199
 
                 gboolean printAppName)
 
164
                 LogHandlerData *data)
200
165
{
201
166
   char *msg = NULL;
202
 
   char *msgCurr = NULL;
203
167
   const char *slevel;
204
168
   size_t len = 0;
205
169
 
244
208
      slevel = "unknown";
245
209
   }
246
210
 
247
 
   if (timestamp) {
 
211
   if (data->timestamp) {
248
212
      char *tstamp;
249
213
 
250
214
      tstamp = System_GetTimeAsString();
251
 
      if (printAppName) {
 
215
      if (data->shared) {
252
216
         len = VMToolsAsprintf(&msg, "[%s] [%8s] [%s:%s] %s\n",
253
217
                               (tstamp != NULL) ? tstamp : "no time",
254
218
                               slevel, gLogDomain, domain, message);
259
223
      }
260
224
      free(tstamp);
261
225
   } else {
262
 
      if (printAppName) {
 
226
      if (data->shared) {
263
227
         len = VMToolsAsprintf(&msg, "[%8s] [%s:%s] %s\n",
264
228
                               slevel, gLogDomain, domain, message);
265
229
      } else {
267
231
      }
268
232
   }
269
233
 
270
 
   if (msg != NULL) {
 
234
   if (msg != NULL && data->convertToLocal) {
271
235
      size_t msgCurrLen;
272
 
      msgCurr = g_locale_from_utf8(msg, strlen(msg), NULL, &msgCurrLen, NULL);
 
236
      gchar *msgCurr = g_locale_from_utf8(msg, strlen(msg), NULL, &msgCurrLen, NULL);
273
237
 
274
238
      /*
275
239
       * The log messages from glib itself (and probably other libraries based
280
244
      if (msgCurr != NULL && msgCurr[msgCurrLen - 2] == '\n') {
281
245
         msgCurr[msgCurrLen - 1] = '\0';
282
246
      }
283
 
   }
284
247
 
285
 
   if (msgCurr != NULL) {
286
248
      g_free(msg);
287
 
      return msgCurr;
 
249
      msg = msgCurr;
288
250
   }
 
251
 
289
252
   return msg;
290
253
}
291
254
 
292
255
 
293
256
/**
 
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
 
259
 * about that error.
 
260
 *
 
261
 * @param[in] domain    Log domain.
 
262
 * @param[in] level     Log level.
 
263
 * @param[in] fmt       Message format.
 
264
 * @param[in] ...       Message parameters.
 
265
 */
 
266
 
 
267
static void
 
268
VMToolsError(const gchar *domain,
 
269
             GLogLevelFlags level,
 
270
             const gchar *fmt,
 
271
             ...)
 
272
{
 
273
   gchar *message;
 
274
   gchar *formatted;
 
275
   va_list args;
 
276
 
 
277
   va_start(args, fmt);
 
278
   g_vasprintf(&message, fmt, args);
 
279
   va_end(args);
 
280
 
 
281
   formatted = VMToolsLogFormat(message, domain, level, gErrorData);
 
282
 
 
283
   gErrorData->logfn(domain, level, formatted, gErrorData, VMToolsError);
 
284
   g_free(formatted);
 
285
   g_free(message);
 
286
}
 
287
 
 
288
 
 
289
/**
294
290
 * Aborts the program, optionally creating a core dump.
295
291
 */
296
292
 
297
293
static INLINE NORETURN void
298
294
VMToolsLogPanic(void)
299
295
{
 
296
   gPanicCount++;
300
297
   if (gEnableCoreDump) {
301
 
#if defined(G_PLATFORM_WIN32)
 
298
#if defined(_WIN32)
302
299
      CoreDump_CoreDump();
303
300
#else
 
301
      char cwd[PATH_MAX];
 
302
      if (getcwd(cwd, sizeof cwd) != NULL) {
 
303
         if (access(cwd, W_OK) == -1) {
 
304
            /*
 
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.
 
307
             */
 
308
            const char *home = getenv("HOME");
 
309
            if (home != NULL) {
 
310
               if (chdir(home)) {
 
311
                  /* Just to make glibc headers happy. */
 
312
               }
 
313
            }
 
314
         }
 
315
      }
304
316
      abort();
305
317
#endif
306
318
   }
309
321
}
310
322
 
311
323
 
312
 
#if defined(G_PLATFORM_WIN32)
313
 
/**
314
 
 * Logs a message to OutputDebugString.
315
 
 *
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.
320
 
 */
321
 
 
322
 
static void
323
 
VMToolsLogOutputDebugString(const gchar *domain,
324
 
                            GLogLevelFlags level,
325
 
                            const gchar *message,
326
 
                            gpointer _data)
327
 
{
328
 
   LogHandlerData *data = _data;
329
 
   if (SHOULD_LOG(level, data)) {
330
 
      char *msg = VMToolsLogFormat(message, domain, level, FALSE, TRUE);
331
 
      if (msg != NULL) {
332
 
         OutputDebugStringA(msg);
333
 
         g_free(msg);
334
 
      }
335
 
   }
336
 
   if (IS_FATAL(level)) {
337
 
      VMToolsLogPanic();
338
 
   }
339
 
}
340
 
#endif
341
 
 
342
 
 
343
 
/**
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.
347
 
 *
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.
352
 
 */
353
 
 
354
 
static void
355
 
VMToolsLogFile(const gchar *domain,
356
 
               GLogLevelFlags level,
357
 
               const gchar *message,
358
 
               gpointer _data)
359
 
{
360
 
   LogHandlerData *data = _data;
361
 
   if (SHOULD_LOG(level, data)) {
 
324
/**
 
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.
 
327
 *
 
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.
 
332
 */
 
333
 
 
334
static void
 
335
VMToolsLog(const gchar *domain,
 
336
           GLogLevelFlags level,
 
337
           const gchar *message,
 
338
           gpointer _data)
 
339
{
 
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) {
366
 
            /*
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.
372
 
             */
373
 
            gchar warn[1024];
374
 
            g_snprintf(warn, sizeof warn,
375
 
                       "Unable to open log file %s for domain %s.\n",
376
 
                       data->path, data->domain);
377
 
 
378
 
            data->error = TRUE;
379
 
            DEFAULT_HANDLER(domain, G_LOG_LEVEL_WARNING | G_LOG_FLAG_RECURSION,
380
 
                            warn, gDefaultData);
381
 
         }
382
 
      }
383
 
      if (!(level & G_LOG_FLAG_RECURSION) && data->error) {
384
 
         DEFAULT_HANDLER(domain, level | G_LOG_FLAG_RECURSION, message, gDefaultData);
385
 
      } else {
386
 
         gchar *msg = VMToolsLogFormat(message, domain, level, TRUE, FALSE);
387
 
         if (msg != NULL) {
388
 
            FILE *dest = (data->file != NULL) ? data->file
389
 
                           : ((level < G_LOG_LEVEL_MESSAGE) ? stderr : stdout);
390
 
            fputs(msg, dest);
391
 
            fflush(dest);
392
 
            g_free(msg);
393
 
         }
394
 
      }
 
344
      if (!data->logfn(domain, level, msg, data, VMToolsError)) {
 
345
         /*
 
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
 
348
          * errors.
 
349
          */
 
350
         VMToolsError(domain, level | G_LOG_FLAG_RECURSION, message,
 
351
                      gErrorData, VMToolsError);
 
352
      }
 
353
      g_free(msg);
395
354
   }
396
355
   if (IS_FATAL(level)) {
397
356
      VMToolsLogPanic();
420
379
{
421
380
   gchar *handler = NULL;
422
381
   gchar *level = NULL;
423
 
   gchar *logpath = NULL;
424
382
   gchar key[128];
 
383
   guint hid;
 
384
   size_t i;
425
385
 
426
 
   GLogFunc handlerFn = NULL;
427
386
   GLogLevelFlags levelsMask;
 
387
   LogHandlerConfigFn configfn = NULL;
428
388
   LogHandlerData *data;
429
389
 
430
390
   /* Arbitrary limit. */
450
410
   g_snprintf(key, sizeof key, "%s.handler", domain);
451
411
   handler = g_key_file_get_string(cfg, LOGGING_GROUP, key, NULL);
452
412
 
453
 
   if (handler == NULL) {
454
 
      if (strcmp(domain, gLogDomain) == 0) {
455
 
         handlerFn = DEFAULT_HANDLER;
456
 
      } else {
457
 
         handlerFn = gDefaultLogFunc;
458
 
      }
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);
470
 
            goto exit;
471
 
         } else {
472
 
            /*
473
 
             * Do some variable expansion in the input string. Currently only
474
 
             * ${USER} and ${PID} are expanded.
475
 
             */
476
 
            gchar *vars[] = {
477
 
               "${USER}",  NULL,
478
 
               "${PID}",   NULL
479
 
            };
480
 
            size_t i;
481
 
 
482
 
            vars[1] = Hostinfo_GetUser();
483
 
            vars[3] = g_strdup_printf("%"FMTPID, getpid());
484
 
 
485
 
            for (i = 0; i < ARRAYSIZE(vars); i += 2) {
486
 
               char *last = logpath;
487
 
               char *start;
488
 
               while ((start = strstr(last, vars[i])) != NULL) {
489
 
                  gchar *tmp;
490
 
                  char *end = start + strlen(vars[i]);
491
 
                  size_t offset = (start - last) + strlen(vars[i+1]);
492
 
 
493
 
                  *start = '\0';
494
 
                  tmp = g_strdup_printf("%s%s%s", logpath, vars[i+1], end);
495
 
                  g_free(logpath);
496
 
                  logpath = tmp;
497
 
                  last = logpath + offset;
498
 
               }
499
 
            }
500
 
 
501
 
            free(vars[1]);
502
 
            g_free(vars[3]);
 
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;
 
419
            break;
503
420
         }
504
421
      }
505
 
#if defined(G_PLATFORM_WIN32)
506
 
   } else if (strcmp(handler, "outputdebugstring") == 0) {
507
 
      handlerFn = VMToolsLogOutputDebugString;
508
 
#endif
 
422
 
 
423
      if (configfn == NULL) {
 
424
         g_warning("Unknown log handler '%s', using default.", handler);
 
425
         goto exit;
 
426
      }
 
427
 
 
428
      data = configfn(domain, handler, cfg);
 
429
   } else if (strcmp(domain, gLogDomain) == 0) {
 
430
      /*
 
431
       * If no handler defined and we're configuring the default domain,
 
432
       * then instantiate the default handler.
 
433
       */
 
434
      hid = DEFAULT_HANDLER->id;
 
435
      configfn = DEFAULT_HANDLER->configfn;
 
436
      data = configfn(domain, NULL, cfg);
 
437
      ASSERT(data != NULL);
509
438
   } else {
510
 
      g_warning("Unknown log handler: %s\n", handler);
511
 
      goto exit;
 
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;
 
448
      hid = -1;
512
449
   }
513
450
 
 
451
   ASSERT(data->logfn != NULL);
 
452
   ASSERT(data->dtor != NULL);
 
453
 
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;
541
481
      goto exit;
542
482
   }
543
483
 
544
 
   data = g_malloc0(sizeof *data);
545
484
   data->domain = g_strdup(domain);
546
485
   data->mask = levelsMask;
547
 
   data->path = logpath;
548
 
   data->append = (handler != NULL && strcmp(handler, "file+") == 0);
549
 
   logpath = NULL;
 
486
   data->type = hid;
550
487
 
551
488
   if (strcmp(domain, gLogDomain) == 0) {
552
489
      /*
556
493
       */
557
494
      LogHandlerData *old = gDefaultData;
558
495
 
559
 
      if (old != NULL && old->file != NULL) {
560
 
         ASSERT(old->path);
561
 
         if (data->path != NULL && strcmp(data->path, old->path) == 0) {
562
 
            g_free(data->path);
563
 
            data->file = old->file;
564
 
            data->path = old->path;
565
 
            old->path = NULL;
566
 
         } else {
567
 
            fclose(old->file);
568
 
            g_free(old->path);
569
 
         }
 
496
      if (old != NULL && old->type == data->type && old->copyfn != NULL) {
 
497
         data->copyfn(data, old);
570
498
      }
571
499
 
572
 
      g_log_set_default_handler(handlerFn, data);
 
500
      g_log_set_default_handler(VMToolsLog, data);
573
501
      gDefaultData = data;
 
502
      CLEAR_LOG_HANDLER(old);
574
503
      data = NULL;
575
 
      gDefaultLogFunc = handlerFn;
576
 
      g_free(old);
577
 
   } else if (handler == NULL) {
578
 
      ASSERT(data->file == NULL);
579
 
      data->inherited = TRUE;
580
504
   }
581
505
 
582
506
   if (data != NULL) {
588
512
                                          G_LOG_LEVEL_MASK |
589
513
                                          G_LOG_FLAG_FATAL |
590
514
                                          G_LOG_FLAG_RECURSION,
591
 
                                          handlerFn, data);
 
515
                                          VMToolsLog,
 
516
                                          data);
592
517
   }
593
518
 
594
519
exit:
595
520
   g_free(handler);
596
 
   g_free(logpath);
597
521
   g_free(level);
598
522
}
599
523
 
613
537
   gLogEnabled = FALSE;
614
538
   g_log_set_default_handler(g_log_default_handler, NULL);
615
539
 
 
540
   CLEAR_LOG_HANDLER(gErrorData);
 
541
   gErrorData = NULL;
 
542
 
616
543
   if (gDomains != NULL) {
617
544
      guint i;
618
545
      for (i = 0; i < gDomains->len; i++) {
620
547
         g_log_remove_handler(data->domain, data->handlerId);
621
548
         if (hard) {
622
549
            CLEAR_LOG_HANDLER(data);
623
 
            g_free(data);
624
550
         }
625
551
      }
626
552
      if (hard) {
631
557
 
632
558
   if (hard && gDefaultData != NULL) {
633
559
      CLEAR_LOG_HANDLER(gDefaultData);
634
 
      g_free(gDefaultData);
635
560
      gDefaultData = NULL;
636
561
   }
637
562
 
639
564
      g_free(gLogDomain);
640
565
      gLogDomain = NULL;
641
566
   }
642
 
 
643
 
   gDefaultLogFunc = DEFAULT_HANDLER;
644
567
}
645
568
 
646
569
 
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);
682
604
               }
683
605
               break;
684
606
            }
686
608
      }
687
609
   }
688
610
 
689
 
   if (gDefaultData != NULL && oldDefault != NULL) {
690
 
      if (gDefaultData->path != NULL && oldDefault->file != NULL) {
691
 
         ASSERT(gDefaultData->file == NULL);
692
 
         gDefaultData->file = oldDefault->file;
693
 
         oldDefault->file = NULL;
694
 
      }
 
611
   if (gDefaultData != NULL &&
 
612
       oldDefault != NULL &&
 
613
       gDefaultData->copyfn != NULL &&
 
614
       gDefaultData->type == oldDefault->type) {
 
615
      gDefaultData->copyfn(gDefaultData, oldDefault);
695
616
   }
696
617
 
697
618
   /* Second, clean up the old configuration data. */
700
621
         LogHandlerData *data = g_ptr_array_remove_index_fast(oldDomains,
701
622
                                                              oldDomains->len - 1);
702
623
         CLEAR_LOG_HANDLER(data);
703
 
         g_free(data);
704
624
      }
705
625
   }
706
626
 
712
632
 
713
633
/* Public API. */
714
634
 
 
635
 
 
636
#if defined(_WIN32)
 
637
/**
 
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.
 
641
 *
 
642
 * It's safe to call this function multiple times (it won't do anything if
 
643
 * the process already has a console).
 
644
 *
 
645
 * @note Attaching to the parent process's console is only available on XP and
 
646
 * later.
 
647
 *
 
648
 * @return Whether the process is attached to a console.
 
649
 */
 
650
 
 
651
gboolean
 
652
VMTools_AttachConsole(void)
 
653
{
 
654
   typedef BOOL (WINAPI *AttachConsoleFn)(DWORD);
 
655
   gboolean ret = FALSE;
 
656
   AttachConsoleFn _AttachConsole;
 
657
 
 
658
   if (GetConsoleWindow() != NULL) {
 
659
      return TRUE;
 
660
   }
 
661
 
 
662
   _AttachConsole = (AttachConsoleFn) GetProcAddress(GetModuleHandleW(L"kernel32.dll"),
 
663
                                                     "AttachConsole");
 
664
   if ((_AttachConsole != NULL && _AttachConsole(ATTACH_PARENT_PROCESS)) ||
 
665
       AllocConsole()) {
 
666
      FILE* fptr;
 
667
 
 
668
      fptr = _wfreopen(L"CONOUT$", L"a", stdout);
 
669
      if (fptr == NULL) {
 
670
         g_warning("_wfreopen failed for stdout/CONOUT$: %d (%s)",
 
671
                   errno, strerror(errno));
 
672
         goto exit;
 
673
      }
 
674
 
 
675
      fptr = _wfreopen(L"CONOUT$", L"a", stderr);
 
676
      if (fptr == NULL) {
 
677
         g_warning("_wfreopen failed for stderr/CONOUT$: %d (%s)",
 
678
                   errno, strerror(errno));
 
679
         goto exit;
 
680
      }
 
681
      setvbuf(fptr, NULL, _IONBF, 0);
 
682
      ret = TRUE;
 
683
   }
 
684
 
 
685
exit:
 
686
   if (!ret) {
 
687
      g_warning("Console redirection unavailable.");
 
688
   }
 
689
   return ret;
 
690
}
 
691
#endif
 
692
 
 
693
 
715
694
/**
716
695
 * Configures the logging system according to the configuration in the given
717
696
 * dictionary.
734
713
                      gboolean force,
735
714
                      gboolean reset)
736
715
{
 
716
   gboolean allocDict = (cfg == NULL);
737
717
   gchar **list;
738
718
   gchar **curr;
739
719
   GPtrArray *oldDomains = NULL;
741
721
 
742
722
   g_return_if_fail(defaultDomain != NULL);
743
723
 
 
724
   if (allocDict) {
 
725
      cfg = g_key_file_new();
 
726
   }
 
727
 
744
728
   /*
745
729
    * If not resetting the logging system, keep the old domains around. After
746
730
    * we're done loading the new configuration, we'll go through the old domains
755
739
   }
756
740
 
757
741
   gLogDomain = g_strdup(defaultDomain);
758
 
 
759
 
   /*
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.
763
 
    */
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 |
769
 
                           G_LOG_LEVEL_WARNING;
770
 
#if defined(VMX86_DEBUG)
771
 
      gDefaultData->mask |= G_LOG_LEVEL_MESSAGE;
772
 
#endif
773
 
      g_log_set_default_handler(gDefaultLogFunc, gDefaultData);
774
 
      goto exit;
775
 
   }
 
742
   gErrorData = DEFAULT_HANDLER->configfn(gLogDomain, NULL, NULL);
776
743
 
777
744
   /*
778
745
    * Configure the default domain first. See function documentation for
809
776
                                               "enableCoreDump", NULL);
810
777
   }
811
778
 
812
 
exit:
 
779
   /*
 
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.
 
785
    *
 
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
 
791
    * library.
 
792
    *
 
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,
 
795
    * it's set to 5MB.
 
796
    */
 
797
   if (gEnableCoreDump) {
 
798
#if defined(_WIN32)
 
799
      CoreDump_SetUnhandledExceptionFilter();
 
800
#else
 
801
      GError *err = NULL;
 
802
      struct rlimit limit = { 0, 0 };
 
803
 
 
804
      getrlimit(RLIMIT_CORE, &limit);
 
805
      if (limit.rlim_max != 0) {
 
806
         limit.rlim_cur = (rlim_t) g_key_file_get_integer(cfg,
 
807
                                                          LOGGING_GROUP,
 
808
                                                          "maxCoreSize",
 
809
                                                          &err);
 
810
         if (err != NULL) {
 
811
            limit.rlim_cur = 5 * 1024 * 1024;
 
812
            g_clear_error(&err);
 
813
         } else if (limit.rlim_cur == 0) {
 
814
            limit.rlim_cur = RLIM_INFINITY;
 
815
         }
 
816
 
 
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));
 
821
         } else {
 
822
            g_message("Core dump limit set to %d", (int) limit.rlim_cur);
 
823
         }
 
824
      }
 
825
#endif
 
826
   }
 
827
 
813
828
   /* If needed, restore the old configuration. */
814
829
   if (!reset) {
815
830
      VMToolsRestoreLogging(oldDefault, oldDomains);
816
 
      g_free(oldDefault);
817
831
      if (oldDomains != NULL) {
818
832
         g_ptr_array_free(oldDomains, TRUE);
819
833
      }
820
834
   }
821
835
 
822
836
   gLogEnabled |= force;
 
837
 
 
838
   if (allocDict) {
 
839
      g_key_file_free(cfg);
 
840
   }
823
841
}
824
842
 
825
843
 
873
891
{
874
892
   va_list args;
875
893
 
876
 
   gPanicCount++;
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);
 
897
      /*
 
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.
 
900
       */
 
901
      VMToolsLogPanic();
 
902
   } else if (gPanicCount == 1) {
 
903
      /*
 
904
       * Use a stack allocated string since we're in a recursive panic, so
 
905
       * probably already in a weird state.
 
906
       */
 
907
      gchar msg[1024];
 
908
      g_vsnprintf(msg, sizeof msg, fmt, args);
 
909
      fprintf(stderr, "Recursive panic: %s\n", msg);
 
910
      VMToolsLogPanic();
880
911
   } else {
881
 
      char *msg;
882
 
      g_vasprintf(&msg, fmt, args);
883
 
      if (gPanicCount == 2) {
884
 
         fprintf(stderr, "Recursive panic: %s\n", msg);
885
 
      } else {
886
 
         fprintf(stderr, "Recursive panic, giving up: %s\n", msg);
887
 
         exit(-1);
888
 
      }
889
 
      g_free(msg);
 
912
      fprintf(stderr, "Recursive panic, giving up.\n");
 
913
      exit(-1);
890
914
   }
891
915
   va_end(args);
892
 
   /*
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.
895
 
    */
896
 
   VMToolsLogPanic();
897
916
}
898
917
 
899
918