VirtualBox

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

Last change on this file since 99658 was 99658, checked in by vboxsync, 13 months ago

Guest Additions/VBoxClient: Added "--session-detect" command to only perform a display server detection (w/ exit code set). Useful for (user) diagnosis + testcase. bugref:10427

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

© 2023 Oracle
ContactPrivacy policyTerms of Use