~n-muench/ubuntu/precise/open-vm-tools/open-vm-tools.raring-precise.backport

« back to all changes in this revision

Viewing changes to lib/glibUtils/fileLogger.c

  • Committer: Package Import Robot
  • Author(s): Nate Muench
  • Date: 2012-01-23 16:09:45 UTC
  • mfrom: (1.4.6) (2.4.26 sid)
  • Revision ID: package-import@ubuntu.com-20120123160945-b6s0r1vkcovucpf3
Tags: 2011.12.20-562307-0ubuntu1
* Merge latest upstream git tag. Fixes building on Precise
  (LP: #898289, LP: #905612)

* Items merged from Debian unstable:
  - debian/control:
    + open-vm-tools recommends open-vm-dkms. (LP: #598933)
    + open-vm-tools now suggests open-vm-toolbox. (LP: #604998)
  (From 2011.08.21-471295-1 release)
  - Updating maintainer and uploaders fields.
  - Removing vcs fields.
  - Removing references to Daniel's old email address.
  - Updating years in copyright file.
  - Updating to standards version 3.9.2.
  - Updating to debhelper version 8.
  - Switching to source format 3.0 (quilt).
  - Removing manual chrpath setting.
  - Removing exclusion from plugins from debhelper shlibs.
  - Rediffing kvers.patch.
  (From 2011.09.23-491607-1 release)
  - Marking binary architecture-dependend packages as linux and kfreebsd
  only.
  - Removing liburiparser-dev from build-depends as upstream dropped
  unity support.
  - Building with libproc-dev on amd64 again.
  - Dropping disabling of dnet support.
  (From 2011.09.23-491607-2 release)
  - Adding doxygen to build-depends for api documentation.
  - Adding libcunit1-dev to build-depends for test suites.
  - Minimizing rules file.
  - Adding open-vm-tools-dev package, containing only the api
    documentation for now.
  (From 2011.09.23-491607-3 release)
  - Sorting overrides in rules alphabetically.
  - Compacting copyright file.
  - Adding udev rule to set timeout for vmware scsi devices
  (From 2011.12.20-562307-1 release)
  - Adding patch to correct typo in upstreams dkms configuration

* Remaining Changes:
  - Remove Stable part of version numbering.
  - debian folder:
    + Re-added open-vm-dkms.postinst & open-vm-dkms.prerm.
      * Allows dkms modules to compile upon installation.
  - debian/control:
    + Re-add open-vm-source and make into a transitional package
      for open-vm-toolbox.
    + Return dependancies that were moved to open-vm-tools back to
      open-vm-toolbox.
  - debian/rules and debian/open-vm-toolbox.lintian-overrides:
    + Make vmware-user-suid-wrapper suid-root
  - debian/rules:
    + Added CFLAGS field with -Wno-deprecated-declarations
      * Will suppress issues with glib 2.31 or later.
    + Add line to copy vmware-xdg-detect-de into place.
    + Install vmware-user.desktop through toolbox package.
  - debian/open-vm-tools.init:
    + Re-add 'modprobe [-r] vmblock'.
    + Add 'modprobe [-r] vmxnet'.
      * Incase it's not loaded during boot.
    + Remove and re-add pcnet32 module
      * Will be done before (remove) and after (readd) vmxnet module
        is added.
      * If vmxnet doesn't exist (aka modules fail to build), pcnet32 can be
        still used for network connectivity.
      * Workaround until a better fix can be done.
  - Re-add gnome-session to debian/local/xautostart.conf
  - Manpages removed (from debian/manpages):
    + vmmemctl.9
    + vmxnet3.9
    + Remove references to manpages that have been removed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*********************************************************
 
2
 * Copyright (C) 2010 VMware, Inc. All rights reserved.
 
3
 *
 
4
 * This program is free software; you can redistribute it and/or modify it
 
5
 * under the terms of the GNU Lesser General Public License as published
 
6
 * by the Free Software Foundation version 2.1 and no later version.
 
7
 *
 
8
 * This program is distributed in the hope that it will be useful, but
 
9
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 
10
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 
11
 * License for more details.
 
12
 *
 
13
 * You should have received a copy of the GNU Lesser General Public License
 
14
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 
15
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 
16
 *
 
17
 *********************************************************/
 
18
 
 
19
/**
 
20
 * @file fileLogger.c
 
21
 *
 
22
 * Logger that uses file streams and provides optional log rotation.
 
23
 */
 
24
 
 
25
#include "glibUtils.h"
 
26
#include <stdio.h>
 
27
#include <string.h>
 
28
#include <glib/gstdio.h>
 
29
#if defined(G_PLATFORM_WIN32)
 
30
#  include <process.h>
 
31
#  include <windows.h>
 
32
#else
 
33
#  include <fcntl.h>
 
34
#  include <unistd.h>
 
35
#endif
 
36
 
 
37
 
 
38
typedef struct FileLogger {
 
39
   GlibLogger     handler;
 
40
   GIOChannel    *file;
 
41
   gchar         *path;
 
42
   gint           logSize;
 
43
   guint64        maxSize;
 
44
   guint          maxFiles;
 
45
   gboolean       append;
 
46
   gboolean       error;
 
47
   GStaticMutex   lock;
 
48
} FileLogger;
 
49
 
 
50
 
 
51
#if !defined(_WIN32)
 
52
/*
 
53
 *******************************************************************************
 
54
 * FileLoggerIsValid --                                                   */ /**
 
55
 *
 
56
 * Checks that the file descriptor backing this logger is still valid.
 
57
 *
 
58
 * This is a racy workaround for an issue with glib code; or, rather, two
 
59
 * issues. The first issue is that we can't intercept G_LOG_FLAG_RECURSION,
 
60
 * and glib just aborts when that happens (see gnome bug 618956). The second
 
61
 * is that if a GIOChannel channel write fails, that calls
 
62
 * g_io_channel_error_from_errno, which helpfully logs stuff, causing recursion.
 
63
 * Don't get me started on why that's, well, at least questionable.
 
64
 *
 
65
 * This is racy because between the check and the actual GIOChannel operation,
 
66
 * the state of the FD may have changed. In reality, since the bug this is
 
67
 * fixing happens in very special situations where code outside this file is
 
68
 * doing weird things like closing random fds, it should be OK.
 
69
 *
 
70
 * We may still get other write errors from the GIOChannel than EBADF, but
 
71
 * those would be harder to work around. Hopefully this handles the most usual
 
72
 * cases.
 
73
 *
 
74
 * See bug 783999 for some details about what triggers the bug.
 
75
 *
 
76
 * @param[in] logger The logger instance.
 
77
 *
 
78
 * @return TRUE if the I/O channel is still valid.
 
79
 *
 
80
 *******************************************************************************
 
81
 */
 
82
 
 
83
static gboolean
 
84
FileLoggerIsValid(FileLogger *logger)
 
85
{
 
86
   if (logger->file != NULL) {
 
87
      int fd = g_io_channel_unix_get_fd(logger->file);
 
88
      return fcntl(fd, F_GETFD) >= 0;
 
89
   }
 
90
 
 
91
   return FALSE;
 
92
}
 
93
 
 
94
#else
 
95
 
 
96
#define FileLoggerIsValid(logger) TRUE
 
97
 
 
98
#endif
 
99
 
 
100
 
 
101
/*
 
102
 *******************************************************************************
 
103
 * FileLoggerGetPath --                                                   */ /**
 
104
 *
 
105
 * Parses the given template file name and expands embedded variables, and
 
106
 * places the log index information at the right position.
 
107
 *
 
108
 * The following variables are expanded:
 
109
 *
 
110
 *    - ${USER}:  user's login name.
 
111
 *    - ${PID}:   current process's pid.
 
112
 *    - ${IDX}:   index of the log file (for rotation).
 
113
 *
 
114
 * @param[in] data         Log handler data.
 
115
 * @param[in] index        Index of the log file.
 
116
 *
 
117
 * @return The expanded log file path.
 
118
 *
 
119
 ******************************************************************************
 
120
 */
 
121
 
 
122
static gchar *
 
123
FileLoggerGetPath(FileLogger *data,
 
124
                  gint index)
 
125
{
 
126
   gboolean hasIndex = FALSE;
 
127
   gchar indexStr[11];
 
128
   gchar *logpath;
 
129
   gchar *vars[] = {
 
130
      "${USER}",  NULL,
 
131
      "${PID}",   NULL,
 
132
      "${IDX}",   indexStr,
 
133
   };
 
134
   gchar *tmp;
 
135
   size_t i;
 
136
 
 
137
   logpath = g_strdup(data->path);
 
138
   vars[1] = (char *) g_get_user_name();
 
139
   vars[3] = g_strdup_printf("%u", (unsigned int) getpid());
 
140
   g_snprintf(indexStr, sizeof indexStr, "%d", index);
 
141
 
 
142
   for (i = 0; i < G_N_ELEMENTS(vars); i += 2) {
 
143
      char *last = logpath;
 
144
      char *start;
 
145
      while ((start = strstr(last, vars[i])) != NULL) {
 
146
         gchar *tmp;
 
147
         char *end = start + strlen(vars[i]);
 
148
         size_t offset = (start - last) + strlen(vars[i+1]);
 
149
 
 
150
         *start = '\0';
 
151
         tmp = g_strdup_printf("%s%s%s", logpath, vars[i+1], end);
 
152
         g_free(logpath);
 
153
         logpath = tmp;
 
154
         last = logpath + offset;
 
155
 
 
156
         /* XXX: ugly, but well... */
 
157
         if (i == 4) {
 
158
            hasIndex = TRUE;
 
159
         }
 
160
      }
 
161
   }
 
162
 
 
163
   g_free(vars[3]);
 
164
 
 
165
   /*
 
166
    * Always make sure we add the index if it's not 0, since that's used for
 
167
    * backing up old log files.
 
168
    */
 
169
   if (index != 0 && !hasIndex) {
 
170
      char *sep = strrchr(logpath, '.');
 
171
      char *pathsep = strrchr(logpath, '/');
 
172
 
 
173
      if (pathsep == NULL) {
 
174
         pathsep = strrchr(logpath, '\\');
 
175
      }
 
176
 
 
177
      if (sep != NULL && sep > pathsep) {
 
178
         *sep = '\0';
 
179
         sep++;
 
180
         tmp = g_strdup_printf("%s.%d.%s", logpath, index, sep);
 
181
      } else {
 
182
         tmp = g_strdup_printf("%s.%d", logpath, index);
 
183
      }
 
184
      g_free(logpath);
 
185
      logpath = tmp;
 
186
   }
 
187
 
 
188
   return logpath;
 
189
}
 
190
 
 
191
 
 
192
/*
 
193
 *******************************************************************************
 
194
 * FileLoggerOpen --                                                      */ /**
 
195
 *
 
196
 * Opens a log file for writing, backing up the existing log file if one is
 
197
 * present. Only one old log file is preserved.
 
198
 *
 
199
 * @note Make sure this function is called with the write lock held.
 
200
 *
 
201
 * @param[in] data   Log handler data.
 
202
 *
 
203
 * @return Log file pointer (NULL on error).
 
204
 *
 
205
 *******************************************************************************
 
206
 */
 
207
 
 
208
static GIOChannel *
 
209
FileLoggerOpen(FileLogger *data)
 
210
{
 
211
   GIOChannel *logfile = NULL;
 
212
   gchar *path;
 
213
 
 
214
   g_return_val_if_fail(data != NULL, NULL);
 
215
   path = FileLoggerGetPath(data, 0);
 
216
 
 
217
   if (g_file_test(path, G_FILE_TEST_EXISTS)) {
 
218
      struct stat fstats;
 
219
      if (g_stat(path, &fstats) > -1) {
 
220
         data->logSize = (gint) fstats.st_size;
 
221
      }
 
222
 
 
223
      if (!data->append || data->logSize >= data->maxSize) {
 
224
         /*
 
225
          * Find the last log file and iterate back, changing the indices as we go,
 
226
          * so that the oldest log file has the highest index (the new log file
 
227
          * will always be index "0"). When not rotating, "maxFiles" is 1, so we
 
228
          * always keep one backup.
 
229
          */
 
230
         gchar *log;
 
231
         guint id;
 
232
         GPtrArray *logfiles = g_ptr_array_new();
 
233
 
 
234
         /*
 
235
          * Find the id of the last log file. The pointer array will hold
 
236
          * the names of all existing log files + the name of the last log
 
237
          * file, which may or may not exist.
 
238
          */
 
239
         for (id = 0; id < data->maxFiles; id++) {
 
240
            log = FileLoggerGetPath(data, id);
 
241
            g_ptr_array_add(logfiles, log);
 
242
            if (!g_file_test(log, G_FILE_TEST_IS_REGULAR)) {
 
243
               break;
 
244
            }
 
245
         }
 
246
 
 
247
         /* Rename the existing log files, increasing their index by 1. */
 
248
         for (id = logfiles->len - 1; id > 0; id--) {
 
249
            gchar *dest = g_ptr_array_index(logfiles, id);
 
250
            gchar *src = g_ptr_array_index(logfiles, id - 1);
 
251
 
 
252
            if (!g_file_test(dest, G_FILE_TEST_IS_DIR) &&
 
253
                (!g_file_test(dest, G_FILE_TEST_EXISTS) ||
 
254
                 g_unlink(dest) == 0)) {
 
255
               g_rename(src, dest);
 
256
            } else {
 
257
               g_unlink(src);
 
258
            }
 
259
         }
 
260
 
 
261
         /* Cleanup. */
 
262
         for (id = 0; id < logfiles->len; id++) {
 
263
            g_free(g_ptr_array_index(logfiles, id));
 
264
         }
 
265
         g_ptr_array_free(logfiles, TRUE);
 
266
         data->logSize = 0;
 
267
         data->append = FALSE;
 
268
      }
 
269
   }
 
270
 
 
271
   logfile = g_io_channel_new_file(path, data->append ? "a" : "w", NULL);
 
272
   g_free(path);
 
273
 
 
274
   if (logfile != NULL) {
 
275
      g_io_channel_set_encoding(logfile, NULL, NULL);
 
276
   }
 
277
 
 
278
   return logfile;
 
279
}
 
280
 
 
281
 
 
282
/*
 
283
 *******************************************************************************
 
284
 * FileLoggerLog --                                                       */ /**
 
285
 *
 
286
 * Logs a message to the configured destination file. Also opens the file for
 
287
 * writing if it hasn't been done yet.
 
288
 *
 
289
 * @param[in] domain    Log domain.
 
290
 * @param[in] level     Log level.
 
291
 * @param[in] message   Message to log.
 
292
 * @param[in] data      File logger.
 
293
 *
 
294
 *******************************************************************************
 
295
 */
 
296
 
 
297
static void
 
298
FileLoggerLog(const gchar *domain,
 
299
              GLogLevelFlags level,
 
300
              const gchar *message,
 
301
              gpointer data)
 
302
{
 
303
   FileLogger *logger = data;
 
304
   gsize written;
 
305
 
 
306
   g_static_mutex_lock(&logger->lock);
 
307
 
 
308
   if (logger->error) {
 
309
      goto exit;
 
310
   }
 
311
 
 
312
   if (logger->file == NULL) {
 
313
      if (logger->file == NULL) {
 
314
         logger->file = FileLoggerOpen(data);
 
315
      }
 
316
      if (logger->file == NULL) {
 
317
         logger->error = TRUE;
 
318
         goto exit;
 
319
      }
 
320
   }
 
321
 
 
322
   if (!FileLoggerIsValid(logger)) {
 
323
      logger->error = TRUE;
 
324
      goto exit;
 
325
   }
 
326
 
 
327
   /* Write the log file and do log rotation accounting. */
 
328
   if (g_io_channel_write_chars(logger->file, message, -1, &written, NULL) ==
 
329
       G_IO_STATUS_NORMAL) {
 
330
      if (logger->maxSize > 0) {
 
331
         logger->logSize += (gint) written;
 
332
         if (logger->logSize >= logger->maxSize) {
 
333
            g_io_channel_unref(logger->file);
 
334
            logger->append = FALSE;
 
335
            logger->file = FileLoggerOpen(logger);
 
336
         } else {
 
337
            g_io_channel_flush(logger->file, NULL);
 
338
         }
 
339
      } else {
 
340
         g_io_channel_flush(logger->file, NULL);
 
341
      }
 
342
   }
 
343
 
 
344
exit:
 
345
   g_static_mutex_unlock(&logger->lock);
 
346
}
 
347
 
 
348
 
 
349
/*
 
350
 ******************************************************************************
 
351
 * FileLoggerDestroy --                                               */ /**
 
352
 *
 
353
 * Cleans up the internal state of a file logger.
 
354
 *
 
355
 * @param[in] _data     File logger data.
 
356
 *
 
357
 ******************************************************************************
 
358
 */
 
359
 
 
360
static void
 
361
FileLoggerDestroy(gpointer data)
 
362
{
 
363
   FileLogger *logger = data;
 
364
   if (logger->file != NULL) {
 
365
      g_io_channel_unref(logger->file);
 
366
   }
 
367
   g_static_mutex_free(&logger->lock);
 
368
   g_free(logger->path);
 
369
   g_free(logger);
 
370
}
 
371
 
 
372
 
 
373
/*
 
374
 *******************************************************************************
 
375
 * GlibUtils_CreateFileLogger --                                          */ /**
 
376
 *
 
377
 * @brief Creates a new file logger based on the given configuration.
 
378
 *
 
379
 * @param[in] path      Path to log file.
 
380
 * @param[in] append    Whether to append to existing log file.
 
381
 * @param[in] maxSize   Maximum log file size (in MB, 0 = no limit).
 
382
 * @param[in] maxFiles  Maximum number of old files to be kept.
 
383
 *
 
384
 * @return A new logger, or NULL on error.
 
385
 *
 
386
 *******************************************************************************
 
387
 */
 
388
 
 
389
GlibLogger *
 
390
GlibUtils_CreateFileLogger(const char *path,
 
391
                           gboolean append,
 
392
                           guint maxSize,
 
393
                           guint maxFiles)
 
394
{
 
395
   FileLogger *data = NULL;
 
396
 
 
397
   g_return_val_if_fail(path != NULL, NULL);
 
398
 
 
399
   data = g_new0(FileLogger, 1);
 
400
   data->handler.addsTimestamp = FALSE;
 
401
   data->handler.shared = FALSE;
 
402
   data->handler.logfn = FileLoggerLog;
 
403
   data->handler.dtor = FileLoggerDestroy;
 
404
 
 
405
   data->path = g_filename_from_utf8(path, -1, NULL, NULL, NULL);
 
406
   if (data->path == NULL) {
 
407
      g_free(data);
 
408
      return NULL;
 
409
   }
 
410
 
 
411
   data->append = append;
 
412
   data->maxSize = maxSize * 1024 * 1024;
 
413
   data->maxFiles = maxFiles + 1; /* To account for the active log file. */
 
414
   g_static_mutex_init(&data->lock);
 
415
 
 
416
   return &data->handler;
 
417
}
 
418