VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/VBoxClient/logging.cpp@ 98103

Last change on this file since 98103 was 98103, checked in by vboxsync, 17 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.5 KB
Line 
1/* $Id: logging.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * VirtualBox Guest Additions - X11 Client.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29#include <sys/wait.h>
30#include <stdlib.h>
31
32#include <iprt/buildconfig.h>
33#include <iprt/file.h>
34#include <iprt/process.h>
35#include <iprt/stream.h>
36#include <iprt/system.h>
37
38#ifdef VBOX_WITH_DBUS
39# include <VBox/dbus.h>
40#endif
41#include <VBox/VBoxGuestLib.h>
42
43#include <package-generated.h>
44#include "VBoxClient.h"
45
46/** Logging parameters. */
47/** @todo Make this configurable later. */
48static PRTLOGGER g_pLoggerRelease = NULL;
49static uint32_t g_cHistory = 10; /* Enable log rotation, 10 files. */
50static uint32_t g_uHistoryFileTime = RT_SEC_1DAY; /* Max 1 day per file. */
51static uint64_t g_uHistoryFileSize = 100 * _1M; /* Max 100MB per file. */
52
53/** Custom log prefix (to be set externally). */
54static char *g_pszCustomLogPrefix;
55
56extern unsigned g_cRespawn;
57
58
59/**
60 * Fallback notification helper using 'notify-send'.
61 *
62 * @returns VBox status code.
63 * @returns VERR_NOT_SUPPORTED if 'notify-send' is not available, or there was an error while running 'notify-send'.
64 * @param pszMessage Message to notify desktop environment with.
65 */
66int vbclNotifyFallbackNotifySend(const char *pszMessage)
67{
68 AssertPtrReturn(pszMessage, VERR_INVALID_POINTER);
69
70 int rc = VINF_SUCCESS;
71
72 if (g_cRespawn == 0)
73 {
74 char *pszCommand = RTStrAPrintf2("notify-send \"VBoxClient: %s\"", pszMessage);
75 if (pszCommand)
76 {
77 int status = system(pszCommand);
78
79 RTStrFree(pszCommand);
80
81 if (WEXITSTATUS(status) != 0) /* Utility or extension not available. */
82 {
83 pszCommand = RTStrAPrintf2("xmessage -buttons OK:0 -center \"VBoxClient: %s\"",
84 pszMessage);
85 if (pszCommand)
86 {
87 status = system(pszCommand);
88 if (WEXITSTATUS(status) != 0) /* Utility or extension not available. */
89 rc = VERR_NOT_SUPPORTED;
90
91 RTStrFree(pszCommand);
92 }
93 else
94 rc = VERR_NO_MEMORY;
95 }
96 }
97 else
98 rc = VERR_NO_MEMORY;
99 }
100
101 return rc;
102}
103
104/**
105 * Shows a notification on the desktop.
106 *
107 * @returns VBox status code.
108 * @returns VERR_NOT_SUPPORTED if the current desktop environment is not supported.
109 * @param pszHeader Header text to show.
110 * @param pszBody Body text to show.
111 *
112 * @note How this notification will look like depends on the actual desktop environment implementing
113 * the actual notification service. Currently only D-BUS-compatible environments are supported.
114 *
115 * Most notification implementations have length limits on their header / body texts, so keep
116 * the text(s) short.
117 */
118int VBClShowNotify(const char *pszHeader, const char *pszBody)
119{
120 AssertPtrReturn(pszHeader, VERR_INVALID_POINTER);
121 AssertPtrReturn(pszBody, VERR_INVALID_POINTER);
122
123 int rc;
124# ifdef VBOX_WITH_DBUS
125 rc = RTDBusLoadLib(); /** @todo Does this init / load the lib only once? Need to check this. */
126 if (RT_FAILURE(rc))
127 {
128 VBClLogError("D-Bus seems not to be installed; no desktop notifications available\n");
129 return rc;
130 }
131
132 DBusConnection *conn;
133 DBusMessage* msg = NULL;
134 conn = dbus_bus_get(DBUS_BUS_SESSION, NULL);
135 if (conn == NULL)
136 {
137 VBClLogError("Could not retrieve D-BUS session bus\n");
138 rc = VERR_INVALID_HANDLE;
139 }
140 else
141 {
142 msg = dbus_message_new_method_call("org.freedesktop.Notifications",
143 "/org/freedesktop/Notifications",
144 "org.freedesktop.Notifications",
145 "Notify");
146 if (msg == NULL)
147 {
148 VBClLogError("Could not create D-BUS message!\n");
149 rc = VERR_INVALID_HANDLE;
150 }
151 else
152 rc = VINF_SUCCESS;
153 }
154 if (RT_SUCCESS(rc))
155 {
156 uint32_t msg_replace_id = 0;
157 const char *msg_app = "VBoxClient";
158 const char *msg_icon = "";
159 const char *msg_summary = pszHeader;
160 const char *msg_body = pszBody;
161 int32_t msg_timeout = -1; /* Let the notification server decide */
162
163 DBusMessageIter iter;
164 DBusMessageIter array;
165 /*DBusMessageIter dict; - unused */
166 /*DBusMessageIter value; - unused */
167 /*DBusMessageIter variant; - unused */
168 /*DBusMessageIter data; - unused */
169
170 /* Format: UINT32 org.freedesktop.Notifications.Notify
171 * (STRING app_name, UINT32 replaces_id, STRING app_icon, STRING summary, STRING body,
172 * ARRAY actions, DICT hints, INT32 expire_timeout)
173 */
174 dbus_message_iter_init_append(msg,&iter);
175 dbus_message_iter_append_basic(&iter,DBUS_TYPE_STRING,&msg_app);
176 dbus_message_iter_append_basic(&iter,DBUS_TYPE_UINT32,&msg_replace_id);
177 dbus_message_iter_append_basic(&iter,DBUS_TYPE_STRING,&msg_icon);
178 dbus_message_iter_append_basic(&iter,DBUS_TYPE_STRING,&msg_summary);
179 dbus_message_iter_append_basic(&iter,DBUS_TYPE_STRING,&msg_body);
180 dbus_message_iter_open_container(&iter,DBUS_TYPE_ARRAY,DBUS_TYPE_STRING_AS_STRING,&array);
181 dbus_message_iter_close_container(&iter,&array);
182 dbus_message_iter_open_container(&iter,DBUS_TYPE_ARRAY,"{sv}",&array);
183 dbus_message_iter_close_container(&iter,&array);
184 dbus_message_iter_append_basic(&iter,DBUS_TYPE_INT32,&msg_timeout);
185
186 DBusError err;
187 dbus_error_init(&err);
188
189 DBusMessage *reply;
190 reply = dbus_connection_send_with_reply_and_block(conn, msg, 30 * 1000 /* 30 seconds timeout */, &err);
191 if (dbus_error_is_set(&err))
192 VBClLogError("D-BUS returned an error while sending the notification: %s", err.message);
193 else if (reply)
194 {
195 dbus_connection_flush(conn);
196 dbus_message_unref(reply);
197 }
198 if (dbus_error_is_set(&err))
199 dbus_error_free(&err);
200 }
201 if (msg != NULL)
202 dbus_message_unref(msg);
203# else
204 /** @todo Implement me */
205 RT_NOREF(pszHeader, pszBody);
206 rc = VERR_NOT_SUPPORTED;
207# endif /* VBOX_WITH_DBUS */
208
209 /* Try to use a fallback if the stuff above fails or is not available. */
210 if (RT_FAILURE(rc))
211 rc = vbclNotifyFallbackNotifySend(pszBody);
212
213 /* If everything fails, still print out our notification to stdout, in the hope
214 * someone still gets aware of it. */
215 if (RT_FAILURE(rc))
216 VBClLogInfo("*** Notification: %s - %s ***\n", pszHeader, pszBody);
217
218 return rc;
219}
220
221
222
223/**
224 * Logs a verbose message.
225 *
226 * @param pszFormat The message text.
227 * @param va Format arguments.
228 */
229static void vbClLogV(const char *pszFormat, va_list va)
230{
231 char *psz = NULL;
232 RTStrAPrintfV(&psz, pszFormat, va);
233 AssertPtrReturnVoid(psz);
234 LogRel(("%s", psz));
235 RTStrFree(psz);
236}
237
238/**
239 * Logs a fatal error, notifies the desktop environment via a message and
240 * exits the application immediately.
241 *
242 * @param pszFormat Format string to log.
243 * @param ... Variable arguments for format string. Optional.
244 */
245void VBClLogFatalError(const char *pszFormat, ...)
246{
247 va_list args;
248 va_start(args, pszFormat);
249 char *psz = NULL;
250 RTStrAPrintfV(&psz, pszFormat, args);
251 va_end(args);
252
253 AssertPtrReturnVoid(psz);
254 LogFunc(("Fatal Error: %s", psz));
255 LogRel(("Fatal Error: %s", psz));
256
257 VBClShowNotify("VBoxClient - Fatal Error", psz);
258
259 RTStrFree(psz);
260}
261
262/**
263 * Logs an error message to the (release) logging instance.
264 *
265 * @param pszFormat Format string to log.
266 */
267void VBClLogError(const char *pszFormat, ...)
268{
269 va_list args;
270 va_start(args, pszFormat);
271 char *psz = NULL;
272 RTStrAPrintfV(&psz, pszFormat, args);
273 va_end(args);
274
275 AssertPtrReturnVoid(psz);
276 LogFunc(("Error: %s", psz));
277 LogRel(("Error: %s", psz));
278
279 RTStrFree(psz);
280}
281
282/**
283 * Logs an info message to the (release) logging instance.
284 *
285 * @param pszFormat Format string to log.
286 */
287void VBClLogInfo(const char *pszFormat, ...)
288{
289 va_list args;
290 va_start(args, pszFormat);
291 vbClLogV(pszFormat, args);
292 va_end(args);
293}
294
295/**
296 * Displays a verbose message based on the currently
297 * set global verbosity level.
298 *
299 * @param iLevel Minimum log level required to display this message.
300 * @param pszFormat The message text.
301 * @param ... Format arguments.
302 */
303void VBClLogVerbose(unsigned iLevel, const char *pszFormat, ...)
304{
305 if (iLevel <= g_cVerbosity)
306 {
307 va_list va;
308 va_start(va, pszFormat);
309 vbClLogV(pszFormat, va);
310 va_end(va);
311 }
312}
313
314/**
315 * @callback_method_impl{FNRTLOGPHASE, Release logger callback}
316 */
317static DECLCALLBACK(void) vbClLogHeaderFooter(PRTLOGGER pLoggerRelease, RTLOGPHASE enmPhase, PFNRTLOGPHASEMSG pfnLog)
318{
319 /* Some introductory information. */
320 static RTTIMESPEC s_TimeSpec;
321 char szTmp[256];
322 if (enmPhase == RTLOGPHASE_BEGIN)
323 RTTimeNow(&s_TimeSpec);
324 RTTimeSpecToString(&s_TimeSpec, szTmp, sizeof(szTmp));
325
326 switch (enmPhase)
327 {
328 case RTLOGPHASE_BEGIN:
329 {
330 pfnLog(pLoggerRelease,
331 "VBoxClient %s r%s (verbosity: %u) %s (%s %s) release log\n"
332 "Log opened %s\n",
333 RTBldCfgVersion(), RTBldCfgRevisionStr(), g_cVerbosity, VBOX_BUILD_TARGET,
334 __DATE__, __TIME__, szTmp);
335
336 int vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
337 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
338 pfnLog(pLoggerRelease, "OS Product: %s\n", szTmp);
339 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
340 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
341 pfnLog(pLoggerRelease, "OS Release: %s\n", szTmp);
342 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
343 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
344 pfnLog(pLoggerRelease, "OS Version: %s\n", szTmp);
345 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp));
346 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
347 pfnLog(pLoggerRelease, "OS Service Pack: %s\n", szTmp);
348
349 /* the package type is interesting for Linux distributions */
350 char szExecName[RTPATH_MAX];
351 char *pszExecName = RTProcGetExecutablePath(szExecName, sizeof(szExecName));
352 pfnLog(pLoggerRelease,
353 "Executable: %s\n"
354 "Process ID: %u\n"
355 "Package type: %s"
356#ifdef VBOX_OSE
357 " (OSE)"
358#endif
359 "\n",
360 pszExecName ? pszExecName : "unknown",
361 RTProcSelf(),
362 VBOX_PACKAGE_STRING);
363 break;
364 }
365
366 case RTLOGPHASE_PREROTATE:
367 pfnLog(pLoggerRelease, "Log rotated - Log started %s\n", szTmp);
368 break;
369
370 case RTLOGPHASE_POSTROTATE:
371 pfnLog(pLoggerRelease, "Log continuation - Log started %s\n", szTmp);
372 break;
373
374 case RTLOGPHASE_END:
375 pfnLog(pLoggerRelease, "End of log file - Log started %s\n", szTmp);
376 break;
377
378 default:
379 /* nothing */
380 break;
381 }
382}
383
384static DECLCALLBACK(size_t) vbClLogPrefixCb(PRTLOGGER pLogger, char *pchBuf, size_t cchBuf, void *pvUser)
385{
386 size_t cbPrefix = 0;
387
388 RT_NOREF(pLogger);
389 RT_NOREF(pvUser);
390
391 if (g_pszCustomLogPrefix)
392 {
393 cbPrefix = RT_MIN(strlen(g_pszCustomLogPrefix), cchBuf);
394 memcpy(pchBuf, g_pszCustomLogPrefix, cbPrefix);
395 }
396
397 return cbPrefix;
398}
399
400/**
401 * Creates the default release logger outputting to the specified file.
402 *
403 * Pass NULL to disabled logging.
404 *
405 * @return IPRT status code.
406 * @param pszLogFile Filename for log output. NULL disables custom handling.
407 */
408int VBClLogCreate(const char *pszLogFile)
409{
410 if (!pszLogFile)
411 return VINF_SUCCESS;
412
413 /* Create release logger (stdout + file). */
414 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
415 RTUINT fFlags = RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME | RTLOGFLAGS_PREFIX_CUSTOM;
416#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
417 fFlags |= RTLOGFLAGS_USECRLF;
418#endif
419 int rc = RTLogCreateEx(&g_pLoggerRelease, "VBOXCLIENT_RELEASE_LOG", fFlags, "all",
420 RT_ELEMENTS(s_apszGroups), s_apszGroups, UINT32_MAX /*cMaxEntriesPerGroup*/,
421 0 /*cBufDescs*/, NULL /*paBufDescs*/, RTLOGDEST_STDOUT | RTLOGDEST_USER,
422 vbClLogHeaderFooter, g_cHistory, g_uHistoryFileSize, g_uHistoryFileTime,
423 NULL /*pOutputIf*/, NULL /*pvOutputIfUser*/,
424 NULL /*pErrInfo*/, "%s", pszLogFile ? pszLogFile : "");
425 if (RT_SUCCESS(rc))
426 {
427 /* register this logger as the release logger */
428 RTLogRelSetDefaultInstance(g_pLoggerRelease);
429
430 rc = RTLogSetCustomPrefixCallback(g_pLoggerRelease, vbClLogPrefixCb, NULL);
431 if (RT_FAILURE(rc))
432 VBClLogError("unable to register custom log prefix callback\n");
433
434 /* Explicitly flush the log in case of VBOXSERVICE_RELEASE_LOG=buffered. */
435 RTLogFlush(g_pLoggerRelease);
436 }
437
438 return rc;
439}
440
441/**
442 * Set custom log prefix.
443 *
444 * @param pszPrefix Custom log prefix string.
445 */
446void VBClLogSetLogPrefix(const char *pszPrefix)
447{
448 g_pszCustomLogPrefix = (char *)pszPrefix;
449}
450
451/**
452 * Destroys the currently active logging instance.
453 */
454void VBClLogDestroy(void)
455{
456 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
457}
458
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use