VirtualBox

source: vbox/trunk/src/VBox/Additions/linux/lightdm-greeter/vbox-greeter.cpp@ 98103

Last change on this file since 98103 was 98103, checked in by vboxsync, 23 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: 51.6 KB
Line 
1/* $Id: vbox-greeter.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * vbox-greeter - an own LightDM greeter module supporting auto-logons
4 * controlled by the host.
5 */
6
7/*
8 * Copyright (C) 2012-2023 Oracle and/or its affiliates.
9 *
10 * This file is part of VirtualBox base platform packages, as
11 * available from https://www.virtualbox.org.
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation, in version 3 of the
16 * License.
17 *
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, see <https://www.gnu.org/licenses>.
25 *
26 * SPDX-License-Identifier: GPL-3.0-only
27 */
28
29
30/*********************************************************************************************************************************
31* Header Files *
32*********************************************************************************************************************************/
33#define GLIB_DISABLE_DEPRECATION_WARNINGS 1 /* g_type_init() is deprecated */
34#include <pwd.h>
35#include <syslog.h>
36#include <stdlib.h>
37
38#include <lightdm.h>
39#ifdef VBOX_WITH_FLTK
40# include <FL/Fl.H>
41# include <FL/fl_ask.H> /* Yes, the casing is correct for 1.3.0 -- d'oh. */
42# include <FL/Fl_Box.H>
43# include <FL/Fl_Button.H>
44# include <FL/fl_draw.H> /* Same as above. */
45# include <FL/Fl_Double_Window.H>
46# include <FL/Fl_Input.H>
47# include <FL/Fl_Menu_Button.H>
48# ifdef VBOX_GREETER_WITH_PNG_SUPPORT
49# include <FL/Fl_PNG_Image.H>
50# include <FL/Fl_Shared_Image.H>
51# endif
52# include <FL/Fl_Secret_Input.H>
53#else
54# include <cairo-xlib.h>
55# include <gtk/gtk.h>
56# include <gdk/gdkx.h>
57#endif
58
59#include <package-generated.h>
60#include "product-generated.h"
61
62#include <iprt/assert.h>
63#include <iprt/buildconfig.h>
64#include <iprt/env.h>
65#include <iprt/file.h>
66#include <iprt/getopt.h>
67#include <iprt/initterm.h>
68#include <iprt/mem.h>
69#include <iprt/message.h>
70#include <iprt/path.h>
71#include <iprt/process.h>
72#include <iprt/stream.h>
73#include <iprt/system.h>
74#include <iprt/string.h>
75#include <iprt/thread.h>
76#include <iprt/time.h>
77
78#include <VBox/log.h>
79#include <VBox/VBoxGuestLib.h>
80
81/* The greeter's full name for logging. */
82#define VBOX_MODULE_NAME "vbox-lightdm-greeter"
83
84/* UI elements used in this greeter. */
85#define VBOX_GREETER_UI_WND_GREETER "wnd_greeter"
86
87#define VBOX_GREETER_UI_EDT_USER "edt_username"
88#define VBOX_GREETER_UI_EDT_PASSWORD "edt_password"
89#define VBOX_GREETER_UI_BTN_LOGIN "btn_login"
90#define VBOX_GREETER_UI_LBL_INFO "lbl_info"
91
92/* UI display options. */
93/** Show the restart menu entry / button. */
94#define VBOX_GREETER_UI_SHOW_RESTART RT_BIT(0)
95/** Show the shutdown menu entry / button. */
96#define VBOX_GREETER_UI_SHOW_SHUTDOWN RT_BIT(1)
97/** Show the (customized) top banner. */
98#define VBOX_GREETER_UI_SHOW_BANNER RT_BIT(2)
99/** Enable custom colors */
100#define VBOX_GREETER_UI_USE_THEMING RT_BIT(3)
101
102/** Extracts the 8-bit red component from an uint32_t. */
103#define VBOX_RGB_COLOR_RED(uColor) uColor & 0xFF
104/** Extracts the 8-bit green component from an uint32_t. */
105#define VBOX_RGB_COLOR_GREEN(uColor) (uColor >> 8) & 0xFF
106/** Extracts the 8-bit blue component from an uint32_t. */
107#define VBOX_RGB_COLOR_BLUE(uColor) (uColor >> 16) & 0xFF
108
109#include <VBox/log.h>
110#ifdef VBOX_WITH_GUEST_PROPS
111# include <VBox/HostServices/GuestPropertySvc.h>
112#endif
113
114/** The program name (derived from argv[0]). */
115char *g_pszProgName = (char *)"";
116/** For debugging. */
117#ifdef DEBUG
118 static int g_iVerbosity = 99;
119#else
120 static int g_iVerbosity = 0;
121#endif
122static bool g_fRunning = true;
123
124/** Logging parameters. */
125/** @todo Make this configurable later. */
126static PRTLOGGER g_pLoggerRelease = NULL;
127static uint32_t g_cHistory = 10; /* Enable log rotation, 10 files. */
128static uint32_t g_uHistoryFileTime = RT_SEC_1DAY; /* Max 1 day per file. */
129static uint64_t g_uHistoryFileSize = 100 * _1M; /* Max 100MB per file. */
130
131/**
132 * Context structure which contains all needed
133 * data within callbacks.
134 */
135typedef struct VBOXGREETERCTX
136{
137 /** Pointer to this greeter instance. */
138 LightDMGreeter *pGreeter;
139#ifdef VBOX_WITH_FLTK
140 Fl_Button *pBtnLogin;
141 Fl_Input *pEdtUsername;
142 Fl_Secret_Input *pEdtPassword;
143 Fl_Box *pLblInfo;
144#else
145 /** The GTK builder instance for accessing
146 * the UI elements. */
147 GtkBuilder *pBuilder;
148#endif
149 /** The timeout (in ms) to wait for credentials. */
150 uint32_t uTimeoutMS;
151 /** The starting timestamp (in ms) to calculate
152 * the timeout. */
153 uint64_t uStartMS;
154 /** Timestamp of last abort message. */
155 uint64_t uTsAbort;
156 /** The HGCM client ID. */
157 uint32_t uClientId;
158 /** The credential password. */
159 char *pszPassword;
160} VBOXGREETERCTX, *PVBOXGREETERCTX;
161
162static void vboxGreeterError(const char *pszFormat, ...)
163{
164 va_list va;
165 char *buf;
166 va_start(va, pszFormat);
167 if (RTStrAPrintfV(&buf, pszFormat, va))
168 {
169 RTLogRelPrintf("%s: error: %s", VBOX_MODULE_NAME, buf);
170 RTStrFree(buf);
171 }
172 va_end(va);
173}
174
175static void vboxGreeterLog(const char *pszFormat, ...)
176{
177 if (g_iVerbosity)
178 {
179 va_list va;
180 char *buf;
181 va_start(va, pszFormat);
182 if (RTStrAPrintfV(&buf, pszFormat, va))
183 {
184 /* Only do normal logging in debug mode; could contain
185 * sensitive data! */
186 RTLogRelPrintf("%s: %s", VBOX_MODULE_NAME, buf);
187 RTStrFree(buf);
188 }
189 va_end(va);
190 }
191}
192
193/** @tood Move the following two functions to VbglR3 (also see pam_vbox). */
194#ifdef VBOX_WITH_GUEST_PROPS
195
196/**
197 * Reads a guest property.
198 *
199 * @return IPRT status code.
200 * @param hPAM PAM handle.
201 * @param uClientID Guest property service client ID.
202 * @param pszKey Key (name) of guest property to read.
203 * @param fReadOnly Indicates whether this key needs to be
204 * checked if it only can be read (and *not* written)
205 * by the guest.
206 * @param pszValue Buffer where to store the key's value.
207 * @param cbValue Size of buffer (in bytes).
208 * @param puTimestamp Timestamp of the value
209 * retrieved. Optional.
210 */
211static int vbox_read_prop(uint32_t uClientID,
212 const char *pszKey, bool fReadOnly,
213 char *pszValue, size_t cbValue, uint64_t *puTimestamp)
214{
215 AssertReturn(uClientID, VERR_INVALID_PARAMETER);
216 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
217 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
218 /* puTimestamp is optional. */
219
220 int rc;
221
222 uint64_t u64Timestamp = 0;
223 char *pszValTemp = NULL;
224 char *pszFlags = NULL;
225 /* The buffer for storing the data and its initial size. We leave a bit
226 * of space here in case the maximum values are raised. */
227 void *pvBuf = NULL;
228 uint32_t cbBuf = GUEST_PROP_MAX_VALUE_LEN + GUEST_PROP_MAX_FLAGS_LEN + _1K;
229
230 /* Because there is a race condition between our reading the size of a
231 * property and the guest updating it, we loop a few times here and
232 * hope. Actually this should never go wrong, as we are generous
233 * enough with buffer space. */
234 for (unsigned i = 0; i < 10; i++)
235 {
236 pvBuf = RTMemRealloc(pvBuf, cbBuf);
237 if (pvBuf)
238 {
239 rc = VbglR3GuestPropRead(uClientID, pszKey, pvBuf, cbBuf,
240 &pszValTemp, &u64Timestamp, &pszFlags,
241 &cbBuf);
242 }
243 else
244 rc = VERR_NO_MEMORY;
245
246 switch (rc)
247 {
248 case VERR_BUFFER_OVERFLOW:
249 {
250 /* Buffer too small, try it with a bigger one next time. */
251 cbBuf += _1K;
252 continue; /* Try next round. */
253 }
254
255 default:
256 break;
257 }
258
259 /* Everything except VERR_BUFFER_OVERLOW makes us bail out ... */
260 break;
261 }
262
263 if (RT_SUCCESS(rc))
264 {
265 /* Check security bits. */
266 if (pszFlags)
267 {
268 if ( fReadOnly
269 && !RTStrStr(pszFlags, "RDONLYGUEST"))
270 {
271 /* If we want a property which is read-only on the guest
272 * and it is *not* marked as such, deny access! */
273 rc = VERR_ACCESS_DENIED;
274 }
275 }
276 else /* No flags, no access! */
277 rc = VERR_ACCESS_DENIED;
278
279 if (RT_SUCCESS(rc))
280 {
281 /* If everything went well copy property value to our destination buffer. */
282 if (!RTStrPrintf(pszValue, cbValue, "%s", pszValTemp))
283 rc = VERR_BUFFER_OVERFLOW;
284
285 if (puTimestamp)
286 *puTimestamp = u64Timestamp;
287 }
288 }
289
290#ifdef DEBUG
291 vboxGreeterLog("Read guest property \"%s\"=\"%s\" (Flags: %s, TS: %RU64): %Rrc\n",
292 pszKey, pszValTemp ? pszValTemp : "<None>",
293 pszFlags ? pszFlags : "<None>", u64Timestamp, rc);
294#endif
295
296 if (pvBuf)
297 RTMemFree(pvBuf);
298
299 return rc;
300}
301
302# if 0 /* unused */
303/**
304 * Waits for a guest property to be changed.
305 *
306 * @return IPRT status code.
307 * @param hPAM PAM handle.
308 * @param uClientID Guest property service client ID.
309 * @param pszKey Key (name) of guest property to wait for.
310 * @param uTimeoutMS Timeout (in ms) to wait for the change. Specify
311 * RT_INDEFINITE_WAIT to wait indefinitly.
312 */
313static int vbox_wait_prop(uint32_t uClientID,
314 const char *pszKey, uint32_t uTimeoutMS)
315{
316 AssertReturn(uClientID, VERR_INVALID_PARAMETER);
317 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
318
319 int rc;
320
321 /* The buffer for storing the data and its initial size. We leave a bit
322 * of space here in case the maximum values are raised. */
323 void *pvBuf = NULL;
324 uint32_t cbBuf = MAX_NAME_LEN + MAX_VALUE_LEN + MAX_FLAGS_LEN + _1K;
325
326 for (int i = 0; i < 10; i++)
327 {
328 void *pvTmpBuf = RTMemRealloc(pvBuf, cbBuf);
329 if (pvTmpBuf)
330 {
331 char *pszName = NULL;
332 char *pszValue = NULL;
333 uint64_t u64TimestampOut = 0;
334 char *pszFlags = NULL;
335
336 pvBuf = pvTmpBuf;
337 rc = VbglR3GuestPropWait(uClientID, pszKey, pvBuf, cbBuf,
338 0 /* Last timestamp; just wait for next event */, uTimeoutMS,
339 &pszName, &pszValue, &u64TimestampOut,
340 &pszFlags, &cbBuf, NULL);
341 }
342 else
343 rc = VERR_NO_MEMORY;
344
345 if (rc == VERR_BUFFER_OVERFLOW)
346 {
347 /* Buffer too small, try it with a bigger one next time. */
348 cbBuf += _1K;
349 continue; /* Try next round. */
350 }
351
352 /* Everything except VERR_BUFFER_OVERLOW makes us bail out ... */
353 break;
354 }
355
356 return rc;
357}
358# endif /* unused */
359
360#endif /* VBOX_WITH_GUEST_PROPS */
361
362/**
363 * Checks for credentials provided by the host / HGCM.
364 *
365 * @return IPRT status code. VERR_NOT_FOUND if no credentials are available,
366 * VINF_SUCCESS on successful retrieval or another IPRT error.
367 * @param pCtx Greeter context.
368 */
369static int vboxGreeterCheckCreds(PVBOXGREETERCTX pCtx)
370{
371 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
372
373 static bool s_fCredsNotFoundMsgShown = false;
374 int rc = VbglR3CredentialsQueryAvailability();
375 if (RT_FAILURE(rc))
376 {
377 if (rc != VERR_NOT_FOUND)
378 vboxGreeterError("vboxGreeterCheckCreds: could not query for credentials! rc=%Rrc. Aborting\n", rc);
379 else if (!s_fCredsNotFoundMsgShown)
380 {
381 vboxGreeterLog("vboxGreeterCheckCreds: no credentials available\n");
382 s_fCredsNotFoundMsgShown = true;
383 }
384 }
385 else
386 {
387 /** @todo Domain handling needed? */
388 char *pszUsername; /* User name only is kept local. */
389 char *pszDomain = NULL;
390 rc = VbglR3CredentialsRetrieve(&pszUsername, &pCtx->pszPassword, &pszDomain);
391 if (RT_FAILURE(rc))
392 {
393 vboxGreeterError("vboxGreeterCheckCreds: could not retrieve credentials! rc=%Rrc. Aborting\n", rc);
394 }
395 else
396 {
397 vboxGreeterLog("vboxGreeterCheckCreds: credentials retrieved: user=%s, password=%s, domain=%s\n",
398 pszUsername,
399#ifdef DEBUG
400 pCtx->pszPassword,
401#else
402 "XXX",
403#endif
404 pszDomain);
405 /* Trigger LightDM authentication with the user name just retrieved. */
406 lightdm_greeter_authenticate(pCtx->pGreeter, pszUsername); /* Must be the real user name from host! */
407
408 /* Securely wipe the user name + domain again. */
409 VbglR3CredentialsDestroy(pszUsername, NULL /* pszPassword */, pszDomain,
410 3 /* Three wipe passes */);
411 }
412 }
413
414#ifdef DEBUG
415 vboxGreeterLog("vboxGreeterCheckCreds: returned with rc=%Rrc\n", rc);
416#endif
417 return rc;
418}
419
420/**
421 * Called by LightDM when greeter is not needed anymore.
422 *
423 * @param signum Signal number.
424 */
425static void cb_sigterm(int signum)
426{
427 RT_NOREF(signum);
428
429 /* Note: This handler must be reentrant-safe. */
430#ifdef VBOX_WITH_FLTK
431 g_fRunning = false;
432#else
433 exit(RTEXITCODE_SUCCESS);
434#endif
435}
436
437/**
438 * Callback for showing a user prompt, issued by the LightDM server.
439 *
440 * @param pGreeter Pointer to this greeter instance.
441 * @param pszText Text to display.
442 * @param enmType Type of prompt to display.
443 * @param pvData Pointer to user-supplied data.
444 */
445static void cb_lightdm_show_prompt(LightDMGreeter *pGreeter,
446 const gchar *pszText, LightDMPromptType enmType,
447 gpointer pvData)
448{
449 vboxGreeterLog("cb_lightdm_show_prompt: text=%s, type=%d\n", pszText, enmType);
450
451 PVBOXGREETERCTX pCtx = (PVBOXGREETERCTX)pvData;
452 AssertPtr(pCtx);
453
454 switch (enmType)
455 {
456 case 1: /* Password. */
457 {
458 if (pCtx->pszPassword)
459 {
460 lightdm_greeter_respond(pGreeter, pCtx->pszPassword);
461 }
462 else
463 {
464#ifdef VBOX_WITH_FLTK
465 AssertPtr(pCtx->pEdtPassword);
466 const char *pszPwd = pCtx->pEdtPassword->value();
467#else
468 GtkEntry *pEdtPwd = GTK_ENTRY(gtk_builder_get_object(pCtx->pBuilder, "edt_password"));
469 AssertPtr(pEdtPwd);
470 const gchar *pszPwd = gtk_entry_get_text(pEdtPwd);
471#endif
472 lightdm_greeter_respond(pGreeter, pszPwd);
473 }
474 break;
475 }
476 /** @todo Other fields? */
477
478 default:
479 break;
480 }
481
482 VbglR3CredentialsDestroy(NULL /* pszUsername */, pCtx->pszPassword, NULL /* pszDomain */,
483 3 /* Three wipe passes */);
484 pCtx->pszPassword = NULL;
485}
486
487/**
488 * Callback for showing a message, issued by the LightDM server.
489 *
490 * @param pGreeter Pointer to this greeter instance.
491 * @param pszText Text to display.
492 * @param enmType Type of message to display.
493 * @param pvData Pointer to user-supplied data.
494 */
495static void cb_lightdm_show_message(LightDMGreeter *pGreeter,
496 const gchar *pszText, LightDMPromptType enmType,
497 gpointer pvData)
498{
499 RT_NOREF(pGreeter);
500 vboxGreeterLog("cb_lightdm_show_message: text=%s, type=%d\n", pszText, enmType);
501
502 PVBOXGREETERCTX pCtx = (PVBOXGREETERCTX)pvData;
503 AssertPtrReturnVoid(pCtx);
504
505#ifdef VBOX_WITH_FLTK
506 AssertPtr(pCtx->pLblInfo);
507 pCtx->pLblInfo->copy_label(pszText);
508#else
509 GtkLabel *pLblInfo = GTK_LABEL(gtk_builder_get_object(pCtx->pBuilder, "lbl_info"));
510 AssertPtr(pLblInfo);
511 gtk_label_set_text(pLblInfo, pszText);
512#endif
513}
514
515/**
516 * Callback for authentication completion, issued by the LightDM server.
517 *
518 * @param pGreeter Pointer to this greeter instance.
519 */
520static void cb_lightdm_auth_complete(LightDMGreeter *pGreeter)
521{
522 vboxGreeterLog("cb_lightdm_auth_complete\n");
523
524 const gchar *pszUser = lightdm_greeter_get_authentication_user(pGreeter);
525 vboxGreeterLog("authenticating user: %s\n", pszUser ? pszUser : "<NULL>");
526
527 if (lightdm_greeter_get_is_authenticated(pGreeter))
528 {
529 /** @todo Add non-default session support. */
530 gchar *pszSession = g_strdup(lightdm_greeter_get_default_session_hint(pGreeter));
531 if (pszSession)
532 {
533 vboxGreeterLog("starting session: %s\n", pszSession);
534 GError *pError = NULL;
535 if (!lightdm_greeter_start_session_sync(pGreeter, pszSession, &pError))
536 {
537 vboxGreeterError("unable to start session '%s': %s\n",
538 pszSession, pError ? pError->message : "Unknown error");
539 }
540 else
541 {
542 AssertPtr(pszSession);
543 vboxGreeterLog("session '%s' successfully started\n", pszSession);
544 }
545 if (pError)
546 g_error_free(pError);
547 g_free(pszSession);
548 }
549 else
550 vboxGreeterError("unable to get default session\n");
551 }
552 else
553 vboxGreeterLog("user not authenticated successfully (yet)\n");
554}
555
556/**
557 * Callback for clicking on the "Login" button.
558 *
559 * @param pWidget Widget this callback is bound to.
560 * @param pvData Pointer to user-supplied data.
561 */
562#ifdef VBOX_WITH_FLTK
563void cb_btn_login(Fl_Widget *pWidget, void *pvData)
564#else
565void cb_btn_login(GtkWidget *pWidget, gpointer pvData)
566#endif
567{
568 PVBOXGREETERCTX pCtx = (PVBOXGREETERCTX)pvData;
569 RT_NOREF(pWidget);
570 AssertPtr(pCtx);
571
572#ifdef VBOX_WITH_FLTK
573 AssertPtr(pCtx->pEdtUsername);
574 const char *pszUser = pCtx->pEdtUsername->value();
575 AssertPtr(pCtx->pEdtPassword);
576 const char *pszPwd = pCtx->pEdtPassword->value();
577#else
578 GtkEntry *pEdtUser = GTK_ENTRY(gtk_builder_get_object(pCtx->pBuilder, VBOX_GREETER_UI_EDT_USER));
579 AssertPtr(pEdtUser);
580 const gchar *pszUser = gtk_entry_get_text(pEdtUser);
581
582 GtkEntry *pEdtPwd = GTK_ENTRY(gtk_builder_get_object(pCtx->pBuilder, VBOX_GREETER_UI_EDT_PASSWORD));
583 AssertPtr(pEdtPwd);
584 const gchar *pszPwd = gtk_entry_get_text(pEdtPwd);
585#endif
586
587 /** @todo Add domain handling? */
588 vboxGreeterLog("login button pressed: greeter=%p, user=%s, password=%s\n",
589 pCtx->pGreeter,
590 pszUser ? pszUser : "<NONE>",
591#ifdef DEBUG
592 pszPwd ? pszPwd : "<NONE>");
593#else
594 /* Don't log passwords in release mode! */
595 "XXX");
596#endif
597 if (strlen(pszUser)) /* Only authenticate if username is given. */
598 {
599 lightdm_greeter_respond(pCtx->pGreeter, pszPwd);
600 lightdm_greeter_authenticate(pCtx->pGreeter, pszUser);
601 }
602}
603
604/**
605 * Callback for clicking on the "Menu" button.
606 *
607 * @param pWidget Widget this callback is bound to.
608 * @param pvData Pointer to user-supplied data.
609 */
610#ifdef VBOX_WITH_FLTK
611void cb_btn_menu(Fl_Widget *pWidget, void *pvData)
612#else
613void cb_btn_menu(GtkWidget *pWidget, gpointer pvData)
614#endif
615{
616 RT_NOREF(pWidget, pvData);
617 vboxGreeterLog("menu button pressed\n");
618}
619
620/**
621 * Callback for clicking on the "Restart" button / menu entry.
622 *
623 * @param pWidget Widget this callback is bound to.
624 * @param pvData Pointer to user-supplied data.
625 */
626#ifdef VBOX_WITH_FLTK
627void cb_btn_restart(Fl_Widget *pWidget, void *pvData)
628#else
629void cb_btn_restart(GtkWidget *pWidget, gpointer pvData)
630#endif
631{
632 RT_NOREF(pWidget, pvData);
633 vboxGreeterLog("restart button pressed\n");
634
635 bool fRestart = true;
636#ifdef VBOX_WITH_FLTK
637 int rc = fl_choice("Really restart the system?", "Yes", "No", NULL);
638 fRestart = rc == 0;
639#endif
640
641 if (fRestart)
642 {
643 vboxGreeterLog("restart requested\n");
644#ifndef DEBUG
645 lightdm_restart(NULL);
646#endif
647 }
648}
649
650/**
651 * Callback for clicking on the "Shutdown" button / menu entry.
652 *
653 * @param pWidget Widget this callback is bound to.
654 * @param pvData Pointer to user-supplied data.
655 */
656#ifdef VBOX_WITH_FLTK
657void cb_btn_shutdown(Fl_Widget *pWidget, void *pvData)
658#else
659void cb_btn_shutdown(GtkWidget *pWidget, gpointer pvData)
660#endif
661{
662 RT_NOREF(pWidget, pvData);
663 vboxGreeterLog("shutdown button pressed\n");
664
665 bool fShutdown = true;
666#ifdef VBOX_WITH_FLTK
667 int rc = fl_choice("Really shutdown the system?", "Yes", "No", NULL);
668 fShutdown = rc == 0;
669#endif
670
671 if (fShutdown)
672 {
673 vboxGreeterLog("shutdown requested\n");
674#ifndef DEBUG
675 lightdm_shutdown(NULL);
676#endif
677 }
678}
679
680#ifdef VBOX_WITH_FLTK
681void cb_edt_username(Fl_Widget *pWidget, void *pvData)
682#else
683void cb_edt_username(GtkWidget *pWidget, gpointer pvData)
684#endif
685{
686 RT_NOREF(pWidget);
687 vboxGreeterLog("cb_edt_username called\n");
688
689 PVBOXGREETERCTX pCtx = (PVBOXGREETERCTX)pvData;
690 AssertPtr(pCtx);
691#ifdef VBOX_WITH_FLTK
692 AssertPtr(pCtx->pEdtPassword);
693 Fl::focus(pCtx->pEdtPassword);
694#endif
695}
696
697#ifdef VBOX_WITH_FLTK
698void cb_edt_password(Fl_Widget *pWidget, void *pvData)
699#else
700void cb_edt_password(GtkWidget *pWidget, gpointer pvData)
701#endif
702{
703 RT_NOREF(pWidget, pvData);
704 vboxGreeterLog("cb_edt_password called\n");
705
706 PVBOXGREETERCTX pCtx = (PVBOXGREETERCTX)pvData;
707 AssertPtr(pCtx);
708#ifdef VBOX_WITH_FLTK
709 AssertPtr(pCtx->pBtnLogin);
710 cb_btn_login(pCtx->pBtnLogin, pvData);
711#endif
712}
713
714/**
715 * Callback for the timer event which is checking for new credentials
716 * from the host.
717 *
718 * @param pvData Pointer to user-supplied data.
719 */
720#ifdef VBOX_WITH_FLTK
721static void cb_check_creds(void *pvData)
722#else
723static gboolean cb_check_creds(gpointer pvData)
724#endif
725{
726 PVBOXGREETERCTX pCtx = (PVBOXGREETERCTX)pvData;
727 AssertPtr(pCtx);
728
729#ifdef DEBUG
730 vboxGreeterLog("cb_check_creds called, clientId=%RU32, timeoutMS=%RU32\n",
731 pCtx->uClientId, pCtx->uTimeoutMS);
732#endif
733
734 int rc = VINF_SUCCESS;
735
736#ifdef VBOX_WITH_GUEST_PROPS
737 bool fAbort = false;
738 char szVal[255];
739 if (pCtx->uClientId)
740 {
741 uint64_t tsAbort;
742 rc = vbox_read_prop(pCtx->uClientId,
743 "/VirtualBox/GuestAdd/PAM/CredsWaitAbort",
744 true /* Read-only on guest */,
745 szVal, sizeof(szVal), &tsAbort);
746 switch (rc)
747 {
748 case VINF_SUCCESS:
749# ifdef DEBUG
750 vboxGreeterLog("cb_check_creds: tsAbort %RU64 <-> %RU64\n",
751 pCtx->uTsAbort, tsAbort);
752# endif
753 if (tsAbort != pCtx->uTsAbort)
754 fAbort = true; /* Timestamps differs, abort. */
755 pCtx->uTsAbort = tsAbort;
756 break;
757
758 case VERR_TOO_MUCH_DATA:
759 vboxGreeterError("cb_check_creds: temporarily unable to get abort notification\n");
760 break;
761
762 case VERR_NOT_FOUND:
763 /* Value not found, continue checking for credentials. */
764 break;
765
766 default:
767 vboxGreeterError("cb_check_creds: the abort notification request failed with rc=%Rrc\n", rc);
768 fAbort = true; /* Abort on error. */
769 break;
770 }
771 }
772
773 if (fAbort)
774 {
775 /* Get optional message. */
776 szVal[0] = '\0';
777 int rc2 = vbox_read_prop(pCtx->uClientId,
778 "/VirtualBox/GuestAdd/PAM/CredsMsgWaitAbort",
779 true /* Read-only on guest */,
780 szVal, sizeof(szVal), NULL /* Timestamp. */);
781 if ( RT_FAILURE(rc2)
782 && rc2 != VERR_NOT_FOUND)
783 vboxGreeterError("cb_check_creds: getting wait abort message failed with rc=%Rrc\n", rc2);
784# ifdef VBOX_WITH_FLTK
785 AssertPtr(pCtx->pLblInfo);
786 pCtx->pLblInfo->copy_label(szVal);
787# else /* !VBOX_WITH_FLTK */
788 GtkLabel *pLblInfo = GTK_LABEL(gtk_builder_get_object(pCtx->pBuilder, VBOX_GREETER_UI_LBL_INFO));
789 AssertPtr(pLblInfo);
790 gtk_label_set_text(pLblInfo, szVal);
791# endif /* !VBOX_WITH_FLTK */
792 vboxGreeterLog("cb_check_creds: got notification from host to abort waiting\n");
793 }
794 else
795 {
796#endif /* VBOX_WITH_GUEST_PROPS */
797 rc = vboxGreeterCheckCreds(pCtx);
798 if (RT_SUCCESS(rc))
799 {
800 /* Credentials retrieved. */
801 }
802 else if (rc == VERR_NOT_FOUND)
803 {
804 /* No credentials found, but try next round (if there's
805 * time left for) ... */
806 }
807#ifdef VBOX_WITH_GUEST_PROPS
808 }
809#endif /* VBOX_WITH_GUEST_PROPS */
810
811 if (rc == VERR_NOT_FOUND) /* No credential found this round. */
812 {
813 /* Calculate timeout value left after process has been started. */
814 uint64_t u64Elapsed = RTTimeMilliTS() - pCtx->uStartMS;
815 /* Is it time to bail out? */
816 if (pCtx->uTimeoutMS < u64Elapsed)
817 {
818#ifdef VBOX_WITH_GUEST_PROPS
819 szVal[0] = '\0';
820 int rc2 = vbox_read_prop(pCtx->uClientId,
821 "/VirtualBox/GuestAdd/PAM/CredsMsgWaitTimeout",
822 true /* Read-only on guest */,
823 szVal, sizeof(szVal), NULL /* Timestamp. */);
824 if ( RT_FAILURE(rc2)
825 && rc2 != VERR_NOT_FOUND)
826 vboxGreeterError("cb_check_creds: getting wait timeout message failed with rc=%Rrc\n", rc2);
827# ifdef VBOX_WITH_FLTK
828 AssertPtr(pCtx->pLblInfo);
829 pCtx->pLblInfo->copy_label(szVal);
830# else
831 GtkLabel *pLblInfo = GTK_LABEL(gtk_builder_get_object(pCtx->pBuilder, VBOX_GREETER_UI_LBL_INFO));
832 AssertPtr(pLblInfo);
833 gtk_label_set_text(pLblInfo, szVal);
834# endif
835#endif /* VBOX_WITH_GUEST_PROPS */
836 vboxGreeterLog("cb_check_creds: no credentials retrieved within time (%RU32ms), giving up\n",
837 pCtx->uTimeoutMS);
838 rc = VERR_TIMEOUT;
839 }
840 }
841
842#ifdef DEBUG
843 vboxGreeterLog("cb_check_creds returned with rc=%Rrc\n", rc);
844#endif
845
846 /* At the moment we only allow *one* shot from the host,
847 * so setting credentials in a second attempt won't be possible
848 * intentionally. */
849
850 if (rc == VERR_NOT_FOUND)
851#ifdef VBOX_WITH_FLTK
852 Fl::repeat_timeout(0.5 /* 500 ms */, cb_check_creds, pvData);
853#else
854 return TRUE; /* No credentials found, do another round. */
855
856 return FALSE; /* Remove timer source on every other error / status. */
857#endif
858}
859
860/**
861 * Release logger callback.
862 *
863 * @return IPRT status code.
864 * @param pLoggerRelease
865 * @param enmPhase
866 * @param pfnLog
867 */
868static DECLCALLBACK(void) vboxGreeterLogHeaderFooter(PRTLOGGER pLoggerRelease, RTLOGPHASE enmPhase, PFNRTLOGPHASEMSG pfnLog)
869{
870 /* Some introductory information. */
871 static RTTIMESPEC s_TimeSpec;
872 char szTmp[256];
873 if (enmPhase == RTLOGPHASE_BEGIN)
874 RTTimeNow(&s_TimeSpec);
875 RTTimeSpecToString(&s_TimeSpec, szTmp, sizeof(szTmp));
876
877 switch (enmPhase)
878 {
879 case RTLOGPHASE_BEGIN:
880 {
881 pfnLog(pLoggerRelease,
882 "vbox-greeter %s r%s (verbosity: %d) %s (%s %s) release log\n"
883 "Log opened %s\n",
884 RTBldCfgVersion(), RTBldCfgRevisionStr(), g_iVerbosity, VBOX_BUILD_TARGET,
885 __DATE__, __TIME__, szTmp);
886
887 int vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
888 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
889 pfnLog(pLoggerRelease, "OS Product: %s\n", szTmp);
890 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
891 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
892 pfnLog(pLoggerRelease, "OS Release: %s\n", szTmp);
893 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
894 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
895 pfnLog(pLoggerRelease, "OS Version: %s\n", szTmp);
896 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
897 pfnLog(pLoggerRelease, "OS Service Pack: %s\n", szTmp);
898
899 /* the package type is interesting for Linux distributions */
900 char szExecName[RTPATH_MAX];
901 char *pszExecName = RTProcGetExecutablePath(szExecName, sizeof(szExecName));
902 pfnLog(pLoggerRelease,
903 "Executable: %s\n"
904 "Process ID: %u\n"
905 "Package type: %s"
906#ifdef VBOX_OSE
907 " (OSE)"
908#endif
909 "\n",
910 pszExecName ? pszExecName : "unknown",
911 RTProcSelf(),
912 VBOX_PACKAGE_STRING);
913 break;
914 }
915
916 case RTLOGPHASE_PREROTATE:
917 pfnLog(pLoggerRelease, "Log rotated - Log started %s\n", szTmp);
918 break;
919
920 case RTLOGPHASE_POSTROTATE:
921 pfnLog(pLoggerRelease, "Log continuation - Log started %s\n", szTmp);
922 break;
923
924 case RTLOGPHASE_END:
925 pfnLog(pLoggerRelease, "End of log file - Log started %s\n", szTmp);
926 break;
927
928 default:
929 /* nothing */;
930 }
931}
932
933/**
934 * Creates the default release logger outputting to the specified file.
935 *
936 * @return IPRT status code.
937 * @param pszLogFile Filename for log output. Optional.
938 */
939static int vboxGreeterLogCreate(const char *pszLogFile)
940{
941 /* Create release logger (stdout + file). */
942 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
943 RTUINT fFlags = RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG;
944#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
945 fFlags |= RTLOGFLAGS_USECRLF;
946#endif
947 int rc = RTLogCreateEx(&g_pLoggerRelease, "VBOXGREETER_RELEASE_LOG", fFlags, "all",
948 RT_ELEMENTS(s_apszGroups), s_apszGroups, UINT32_MAX /*cMaxEntriesPerGroup*/,
949 0 /*cBufDescs*/, NULL /*paBufDescs*/, RTLOGDEST_STDOUT,
950 vboxGreeterLogHeaderFooter, g_cHistory, g_uHistoryFileSize, g_uHistoryFileTime,
951 NULL /*pOutputIf*/, NULL /*pvOutputIfUser*/,
952 NULL /*pErrInfo*/, pszLogFile);
953 if (RT_SUCCESS(rc))
954 {
955 /* register this logger as the release logger */
956 RTLogRelSetDefaultInstance(g_pLoggerRelease);
957
958 /* Explicitly flush the log in case of VBOXGREETER_RELEASE_LOG_FLAGS=buffered. */
959 RTLogFlush(g_pLoggerRelease);
960 }
961
962 return rc;
963}
964
965static void vboxGreeterLogDestroy(void)
966{
967 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
968}
969
970static int vboxGreeterUsage(void)
971{
972 RTPrintf("Usage:\n"
973 " %-12s [-h|-?|--help] [-F|--logfile <file>]\n"
974 " [-v|--verbose] [-V|--version]\n", g_pszProgName);
975
976 RTPrintf("\n"
977 " Copyright (C) 2012-" VBOX_C_YEAR " " VBOX_VENDOR "\n");
978
979 return RTEXITCODE_SYNTAX;
980}
981
982int main(int argc, char **argv)
983{
984 int rc = RTR3InitExe(argc, &argv, 0);
985 if (RT_FAILURE(rc))
986 return RTMsgInitFailure(rc);
987 g_pszProgName = RTPathFilename(argv[0]);
988
989 static const RTGETOPTDEF s_aOptions[] =
990 {
991 { "--logfile", 'F', RTGETOPT_REQ_STRING },
992 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
993 { "--version", 'V', RTGETOPT_REQ_NOTHING }
994 };
995
996 char szLogFile[RTPATH_MAX + 128] = "";
997
998 int ch;
999 RTGETOPTUNION ValueUnion;
1000 RTGETOPTSTATE GetState;
1001 RTGetOptInit(&GetState, argc, argv,
1002 s_aOptions, RT_ELEMENTS(s_aOptions),
1003 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1004
1005 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
1006 && RT_SUCCESS(rc))
1007 {
1008 /* For options that require an argument, ValueUnion has received the value. */
1009 switch (ch)
1010 {
1011 case 'F':
1012 if (!RTStrPrintf(szLogFile, sizeof(szLogFile), "%s", ValueUnion.psz))
1013 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to get prepare log file name");
1014 break;
1015
1016 case 'h':
1017 case '?':
1018 return vboxGreeterUsage();
1019
1020 case 'v': /* Raise verbosity. */
1021 g_iVerbosity++;
1022 break;
1023
1024 case 'V': /* Print version and exit. */
1025 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
1026 return RTEXITCODE_SUCCESS;
1027 break; /* Never reached. */
1028
1029 default:
1030 return RTGetOptPrintError(ch, &ValueUnion);
1031 }
1032 }
1033
1034 if (RT_FAILURE(rc))
1035 return RTEXITCODE_SYNTAX;
1036
1037 rc = VbglR3InitUser();
1038 if (RT_FAILURE(rc))
1039 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to init Vbgl (%Rrc)", rc);
1040
1041 rc = vboxGreeterLogCreate(strlen(szLogFile) ? szLogFile : NULL);
1042 if (RT_FAILURE(rc))
1043 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create release log (%s, %Rrc)",
1044 strlen(szLogFile) ? szLogFile : "<None>", rc);
1045
1046 vboxGreeterLog("init\n");
1047
1048 signal(SIGTERM, cb_sigterm);
1049
1050 /** @todo This function already is too long. Move code into
1051 * functions. */
1052
1053 VBOXGREETERCTX ctx;
1054 RT_ZERO(ctx);
1055
1056 /* UI parameters. */
1057 uint32_t uBgColor = 0; /* The background color. */
1058 uint32_t uLogonDlgHdrColor = 0;
1059 uint32_t uLogonDlgBgColor = 0; /* The greeter's dialog color. */
1060 uint32_t uLogonDlgBtnColor = 0; /* The greeter's button color. */
1061
1062#ifdef VBOX_GREETER_WITH_PNG_SUPPORT
1063 char szBannerPath[RTPATH_MAX];
1064#endif
1065
1066 /* By default most UI elements are shown. */
1067 uint32_t uOptsUI = VBOX_GREETER_UI_SHOW_RESTART
1068 | VBOX_GREETER_UI_SHOW_SHUTDOWN;
1069#ifdef VBOX_WITH_GUEST_PROPS
1070 uint32_t uClientId = 0;
1071 rc = VbglR3GuestPropConnect(&uClientId);
1072 if (RT_SUCCESS(rc))
1073 {
1074 vboxGreeterLog("clientId=%RU32\n", uClientId);
1075
1076 ctx.uClientId = uClientId;
1077
1078 char szVal[256];
1079 int rc2 = vbox_read_prop(uClientId,
1080 "/VirtualBox/GuestAdd/Greeter/HideRestart",
1081 true /* Read-only on guest */,
1082 szVal, sizeof(szVal), NULL /* Timestamp. */);
1083 if ( RT_SUCCESS(rc2)
1084 && !RTStrICmp(szVal, "1"))
1085 {
1086 uOptsUI &= ~VBOX_GREETER_UI_SHOW_RESTART;
1087 }
1088
1089 rc2 = vbox_read_prop(uClientId,
1090 "/VirtualBox/GuestAdd/Greeter/HideShutdown",
1091 true /* Read-only on guest */,
1092 szVal, sizeof(szVal), NULL /* Timestamp. */);
1093 if ( RT_SUCCESS(rc2)
1094 && !RTStrICmp(szVal, "1"))
1095 {
1096 uOptsUI &= ~VBOX_GREETER_UI_SHOW_SHUTDOWN;
1097 }
1098
1099# ifdef VBOX_GREETER_WITH_PNG_SUPPORT
1100 /* Load the banner. */
1101 rc2 = vbox_read_prop(uClientId,
1102 "/VirtualBox/GuestAdd/Greeter/BannerPath",
1103 true /* Read-only on guest */,
1104 szBannerPath, sizeof(szBannerPath), NULL /* Timestamp. */);
1105 if (RT_SUCCESS(rc2))
1106 {
1107 if (RTFileExists(szBannerPath))
1108 {
1109 vboxGreeterLog("showing banner from '%s'\n", szBannerPath);
1110 uOptsUI |= VBOX_GREETER_UI_SHOW_BANNER;
1111 }
1112 else
1113 vboxGreeterLog("warning: unable to find banner at '%s', skipping\n", szBannerPath);
1114 }
1115# endif /* VBOX_GREETER_WITH_PNG_SUPPORT */
1116
1117 /* Use theming?. */
1118 rc2 = vbox_read_prop(uClientId,
1119 "/VirtualBox/GuestAdd/Greeter/UseTheming",
1120 true /* Read-only on guest */,
1121 szVal, sizeof(szVal), NULL /* Timestamp. */);
1122 if ( RT_SUCCESS(rc2)
1123 && !RTStrICmp(szVal, "1"))
1124 {
1125 vboxGreeterLog("custom theming enabled\n");
1126 uOptsUI |= VBOX_GREETER_UI_USE_THEMING;
1127 }
1128
1129 if (uOptsUI & VBOX_GREETER_UI_USE_THEMING)
1130 {
1131 /* Get background color. */
1132 rc2 = vbox_read_prop(uClientId,
1133 "/VirtualBox/GuestAdd/Greeter/Theme/BackgroundColor",
1134 true /* Read-only on guest */,
1135 szVal, sizeof(szVal), NULL /* Timestamp. */);
1136 if (RT_SUCCESS(rc2))
1137 {
1138 uBgColor = strtol(szVal, NULL,
1139 /* Change conversion base when having a 0x prefix. */
1140 RTStrStr(szVal, "0x") == szVal ? 0 : 16);
1141 }
1142
1143 /* Logon dialog. */
1144
1145 /* Get header color. */
1146 rc2 = vbox_read_prop(uClientId,
1147 "/VirtualBox/GuestAdd/Greeter/Theme/LogonDialog/HeaderColor",
1148 true /* Read-only on guest */,
1149 szVal, sizeof(szVal), NULL /* Timestamp. */);
1150 if (RT_SUCCESS(rc2))
1151 {
1152 uLogonDlgHdrColor = strtol(szVal, NULL,
1153 /* Change conversion base when having a 0x prefix. */
1154 RTStrStr(szVal, "0x") == szVal ? 0 : 16);
1155 }
1156
1157 /* Get dialog color. */
1158 rc2 = vbox_read_prop(uClientId,
1159 "/VirtualBox/GuestAdd/Greeter/Theme/LogonDialog/BackgroundColor",
1160 true /* Read-only on guest */,
1161 szVal, sizeof(szVal), NULL /* Timestamp. */);
1162 if (RT_SUCCESS(rc2))
1163 {
1164 uLogonDlgBgColor = strtol(szVal, NULL,
1165 /* Change conversion base when having a 0x prefix. */
1166 RTStrStr(szVal, "0x") == szVal ? 0 : 16);
1167 }
1168
1169 /* Get button color. */
1170 rc2 = vbox_read_prop(uClientId,
1171 "/VirtualBox/GuestAdd/Greeter/Theme/LogonDialog/ButtonColor",
1172 true /* Read-only on guest */,
1173 szVal, sizeof(szVal), NULL /* Timestamp. */);
1174 if (RT_SUCCESS(rc2))
1175 {
1176 uLogonDlgBtnColor = strtol(szVal, NULL,
1177 /* Change conversion base when having a 0x prefix. */
1178 RTStrStr(szVal, "0x") == szVal ? 0 : 16);
1179 }
1180 }
1181 }
1182 else
1183 vboxGreeterError("unable to connect to guest property service, rc=%Rrc\n", rc);
1184#endif
1185 vboxGreeterLog("UI options are: %RU32\n", uOptsUI);
1186
1187#ifdef VBOX_WITH_FLTK
1188 int rc2 = Fl::scheme("plastic");
1189 if (!rc2)
1190 vboxGreeterLog("warning: unable to set visual scheme\n");
1191
1192 Fl::visual(FL_DOUBLE | FL_INDEX);
1193 Fl_Double_Window *pWndMain = new Fl_Double_Window(Fl::w(), Fl::h(), "VirtualBox Guest Additions");
1194 AssertPtr(pWndMain);
1195 if (uOptsUI & VBOX_GREETER_UI_USE_THEMING)
1196 pWndMain->color(fl_rgb_color(VBOX_RGB_COLOR_RED(uBgColor),
1197 VBOX_RGB_COLOR_GREEN(uBgColor),
1198 VBOX_RGB_COLOR_BLUE(uBgColor)));
1199 else /* Default colors. */
1200 pWndMain->color(fl_rgb_color(0x73, 0x7F, 0x8C));
1201
1202 Fl_Double_Window *pWndGreeter = new Fl_Double_Window(500, 350);
1203 AssertPtr(pWndGreeter);
1204 pWndGreeter->set_modal();
1205 if (uOptsUI & VBOX_GREETER_UI_USE_THEMING)
1206 pWndGreeter->color(fl_rgb_color(VBOX_RGB_COLOR_RED(uLogonDlgBgColor),
1207 VBOX_RGB_COLOR_GREEN(uLogonDlgBgColor),
1208 VBOX_RGB_COLOR_BLUE(uLogonDlgBgColor)));
1209 else /* Default colors. */
1210 pWndGreeter->color(fl_rgb_color(255, 255, 255));
1211
1212 uint32_t uOffsetX = 130;
1213 /**
1214 * For now we're using a simple Y offset for moving all elements
1215 * down if a banner needs to be shown on top of the greeter. Not
1216 * very clean but does the job. Use some more layouting stuff
1217 * when this gets more complex.
1218 */
1219 uint32_t uOffsetY = 80;
1220
1221# ifdef VBOX_GREETER_WITH_PNG_SUPPORT
1222 fl_register_images();
1223
1224 /** @todo Add basic image type detection based on file
1225 * extension. */
1226
1227 Fl_PNG_Image *pImgBanner = NULL;
1228 if (uOptsUI & VBOX_GREETER_UI_SHOW_BANNER)
1229 {
1230 pImgBanner = new Fl_PNG_Image(szBannerPath);
1231 AssertPtr(pImgBanner);
1232
1233 /** @todo Make the banner size configurable via guest
1234 * properties. For now it's hardcoded to 460 x 90px. */
1235 Fl_Box *pBoxBanner = new Fl_Box(20, uOffsetY, 460, 90, "");
1236 AssertPtr(pBoxBanner);
1237 pBoxBanner->image(pImgBanner);
1238
1239 uOffsetY = 120;
1240 }
1241# endif
1242
1243 Fl_Box *pLblHeader = new Fl_Box(FL_NO_BOX, 242, uOffsetY, 300, 20,
1244 "Desktop Login");
1245 AssertPtr(pLblHeader);
1246
1247 /** Note to use an own font:
1248 * Fl_Font myfnt = FL_FREE_FONT + 1;
1249 * Fl::set_font(myfnt, "MyFont"); */
1250 Fl_Font fntHeader = FL_FREE_FONT;
1251 Fl::set_font(fntHeader, "Courier");
1252
1253 pLblHeader->align(FL_ALIGN_LEFT);
1254 pLblHeader->labelfont(FL_BOLD);
1255 pLblHeader->labelsize(24);
1256 if (uOptsUI & VBOX_GREETER_UI_USE_THEMING)
1257 pLblHeader->labelcolor(fl_rgb_color(VBOX_RGB_COLOR_RED(uLogonDlgHdrColor),
1258 VBOX_RGB_COLOR_GREEN(uLogonDlgHdrColor),
1259 VBOX_RGB_COLOR_BLUE(uLogonDlgHdrColor)));
1260 else /* Default color. */
1261 pLblHeader->labelcolor(fl_rgb_color(0x51, 0x5F, 0x77));
1262 uOffsetY += 40;
1263
1264 /** @todo Add basic NLS support. */
1265
1266 Fl_Input *pEdtUsername = new Fl_Input(uOffsetX, uOffsetY,
1267 300, 20, "User Name");
1268 AssertPtr(pEdtUsername);
1269 pEdtUsername->callback(cb_edt_username, &ctx);
1270 pEdtUsername->when(FL_WHEN_ENTER_KEY_ALWAYS);
1271 Fl::focus(pEdtUsername);
1272 ctx.pEdtUsername = pEdtUsername;
1273
1274 Fl_Secret_Input *pEdtPassword = new Fl_Secret_Input(uOffsetX, uOffsetY + 40,
1275 300, 20, "Password");
1276 AssertPtr(pEdtPassword);
1277 pEdtPassword->callback(cb_edt_password, &ctx);
1278 pEdtPassword->when(FL_WHEN_ENTER_KEY_ALWAYS);
1279 ctx.pEdtPassword = pEdtPassword;
1280
1281 Fl_Button *pBtnLogin = new Fl_Button(uOffsetX, uOffsetY + 70,
1282 100, 40, "Log In");
1283 AssertPtr(pBtnLogin);
1284 pBtnLogin->callback(cb_btn_login, &ctx);
1285 if (uOptsUI & VBOX_GREETER_UI_USE_THEMING)
1286 pBtnLogin->color(fl_rgb_color(VBOX_RGB_COLOR_RED(uLogonDlgBtnColor),
1287 VBOX_RGB_COLOR_GREEN(uLogonDlgBtnColor),
1288 VBOX_RGB_COLOR_BLUE(uLogonDlgBtnColor)));
1289 else /* Default color. */
1290 pBtnLogin->color(fl_rgb_color(255, 255, 255));
1291 ctx.pBtnLogin = pBtnLogin;
1292
1293 Fl_Menu_Button *pBtnMenu = new Fl_Menu_Button(uOffsetX + 120, uOffsetY + 70,
1294 100, 40, "Options");
1295 AssertPtr(pBtnMenu);
1296 pBtnMenu->callback(cb_btn_menu, &ctx);
1297 if (uOptsUI & VBOX_GREETER_UI_USE_THEMING)
1298 pBtnMenu->color(fl_rgb_color(VBOX_RGB_COLOR_RED(uLogonDlgBtnColor),
1299 VBOX_RGB_COLOR_GREEN(uLogonDlgBtnColor),
1300 VBOX_RGB_COLOR_BLUE(uLogonDlgBtnColor)));
1301 else /* Default color. */
1302 pBtnMenu->color(fl_rgb_color(255, 255, 255));
1303
1304 if (uOptsUI & VBOX_GREETER_UI_SHOW_RESTART)
1305 pBtnMenu->add("Restart", "" /* Shortcut */, cb_btn_restart, &ctx, 0 /* Flags */);
1306 if (uOptsUI & VBOX_GREETER_UI_SHOW_SHUTDOWN)
1307 pBtnMenu->add("Shutdown", "" /* Shortcut */, cb_btn_shutdown, &ctx, 0 /* Flags */);
1308
1309 char szLabel[255];
1310 RTStrPrintf(szLabel, sizeof(szLabel), "Oracle VM VirtualBox Guest Additions %sr%s",
1311 RTBldCfgVersion(), RTBldCfgRevisionStr());
1312 Fl_Box *pLblInfo = new Fl_Box(FL_NO_BOX , 50, uOffsetY + 150,
1313 400, 20, szLabel);
1314 AssertPtr(pLblInfo);
1315 ctx.pLblInfo = pLblInfo;
1316
1317 pWndGreeter->end();
1318 pWndGreeter->position((Fl::w() - pWndGreeter->w()) / 2,
1319 (Fl::h() - pWndGreeter->h()) / 2);
1320
1321 pWndMain->fullscreen();
1322 pWndMain->show(argc, argv);
1323 pWndMain->end();
1324
1325 pWndGreeter->show();
1326#else /* !VBOX_WITH_FLTK */
1327 gtk_init(&argc, &argv);
1328
1329 /* Set default cursor */
1330 gdk_window_set_cursor(gdk_get_default_root_window(), gdk_cursor_new(GDK_LEFT_PTR));
1331
1332 GError *pError = NULL;
1333 GtkBuilder *pBuilder = gtk_builder_new();
1334 AssertPtr(pBuilder);
1335 if (!gtk_builder_add_from_file(pBuilder, "/usr/share/xgreeters/vbox-greeter.ui", &pError))
1336 {
1337 AssertPtr(pError);
1338 vboxGreeterError("unable to load UI: %s", pError->message);
1339 return RTEXITCODE_FAILURE;
1340 }
1341
1342 GtkWindow *pWndGreeter = GTK_WINDOW(gtk_builder_get_object(pBuilder, VBOX_GREETER_UI_WND_GREETER));
1343 AssertPtr(pWndGreeter);
1344 GtkButton *pBtnLogin = GTK_BUTTON(gtk_builder_get_object(pBuilder, VBOX_GREETER_UI_BTN_LOGIN));
1345 AssertPtr(pBtnLogin);
1346 GtkLabel *pLblInfo = GTK_LABEL(gtk_builder_get_object(pBuilder, VBOX_GREETER_UI_LBL_INFO));
1347 AssertPtr(pLblInfo);
1348
1349 ctx.pBuilder = pBuilder;
1350
1351 g_signal_connect(G_OBJECT(pBtnLogin), "clicked", G_CALLBACK(cb_btn_login), &ctx);
1352
1353 GdkRectangle rectScreen;
1354 gdk_screen_get_monitor_geometry(gdk_screen_get_default(), gdk_screen_get_primary_monitor(gdk_screen_get_default()), &rectScreen);
1355 vboxGreeterLog("monitor (default) is %dx%d\n", rectScreen.width, rectScreen.height);
1356
1357 gint iWndX, iWndY;
1358 gtk_window_get_default_size(pWndGreeter, &iWndX, &iWndY);
1359 vboxGreeterLog("greeter is %dx%d\n", iWndX, iWndY);
1360
1361 gtk_window_move(pWndGreeter,
1362 (rectScreen.width / 2) - (iWndX / 2),
1363 (rectScreen.height / 2) - (iWndY / 2));
1364 gtk_widget_show(GTK_WIDGET(pWndGreeter));
1365
1366 g_clear_error(&pError);
1367#endif /* !VBOX_WITH_FLTK */
1368
1369 /* GType is needed in any case (for LightDM), whether we
1370 * use GTK3 or not. */
1371 g_type_init();
1372
1373 GMainLoop *pMainLoop = g_main_loop_new(NULL, FALSE /* Not yet running */);
1374 AssertPtr(pMainLoop); NOREF(pMainLoop);
1375
1376 LightDMGreeter *pGreeter = lightdm_greeter_new();
1377 AssertPtr(pGreeter);
1378
1379 g_signal_connect(pGreeter, "show-prompt", G_CALLBACK(cb_lightdm_show_prompt), &ctx);
1380 g_signal_connect(pGreeter, "show-message", G_CALLBACK(cb_lightdm_show_message), &ctx);
1381 g_signal_connect(pGreeter, "authentication-complete", G_CALLBACK(cb_lightdm_auth_complete), &ctx);
1382
1383 ctx.pGreeter = pGreeter;
1384
1385 if (!lightdm_greeter_connect_sync(pGreeter, NULL))
1386 {
1387 vboxGreeterError("unable to connect to LightDM server, aborting\n");
1388 return RTEXITCODE_FAILURE;
1389 }
1390
1391 vboxGreeterLog("connected to LightDM server\n");
1392
1393#ifdef VBOX_WITH_GUEST_PROPS
1394 bool fCheckCreds = false;
1395 if (uClientId) /* Connected to guest property service? */
1396 {
1397 char szVal[256];
1398 rc2 = vbox_read_prop(uClientId,
1399 "/VirtualBox/GuestAdd/PAM/CredsWait",
1400 true /* Read-only on guest */,
1401 szVal, sizeof(szVal), NULL /* Timestamp. */);
1402 if (RT_SUCCESS(rc2))
1403 {
1404 uint32_t uTimeoutMS = RT_INDEFINITE_WAIT; /* Wait infinite by default. */
1405 rc2 = vbox_read_prop(uClientId,
1406 "/VirtualBox/GuestAdd/PAM/CredsWaitTimeout",
1407 true /* Read-only on guest */,
1408 szVal, sizeof(szVal), NULL /* Timestamp. */);
1409 if (RT_SUCCESS(rc2))
1410 {
1411 uTimeoutMS = RTStrToUInt32(szVal);
1412 if (!uTimeoutMS)
1413 {
1414 vboxGreeterError("pam_vbox_authenticate: invalid waiting timeout value specified, defaulting to infinite timeout\n");
1415 uTimeoutMS = RT_INDEFINITE_WAIT;
1416 }
1417 else
1418 uTimeoutMS = uTimeoutMS * 1000; /* Make ms out of s. */
1419 }
1420
1421 ctx.uTimeoutMS = uTimeoutMS;
1422
1423 rc2 = vbox_read_prop(uClientId,
1424 "/VirtualBox/GuestAdd/PAM/CredsMsgWaiting",
1425 true /* Read-only on guest */,
1426 szVal, sizeof(szVal), NULL /* Timestamp. */);
1427 if (RT_SUCCESS(rc2))
1428 {
1429# ifdef VBOX_WITH_FLTK
1430 Assert(pLblInfo);
1431 pLblInfo->copy_label(szVal);
1432# else
1433 gtk_label_set_text(pLblInfo, szVal);
1434# endif
1435 }
1436
1437 /* Get initial timestamp so that we can compare the time
1438 * whether the value has been changed or not in our event callback. */
1439 vbox_read_prop(uClientId,
1440 "/VirtualBox/GuestAdd/PAM/CredsWaitAbort",
1441 true /* Read-only on guest */,
1442 szVal, sizeof(szVal), &ctx.uTsAbort);
1443
1444 if (RT_SUCCESS(rc))
1445 {
1446 /* Before we actuall wait for credentials just make sure we didn't already get credentials
1447 * set so that we can skip waiting for them ... */
1448 rc2 = vboxGreeterCheckCreds(&ctx);
1449 if (rc2 == VERR_NOT_FOUND)
1450 {
1451 /* Get current time stamp to later calculate rest of timeout left. */
1452 ctx.uStartMS = RTTimeMilliTS();
1453
1454 fCheckCreds = true;
1455 }
1456 }
1457 }
1458
1459 /* Start the timer to check credentials availability. */
1460 if (fCheckCreds)
1461 {
1462 vboxGreeterLog("No credentials available on startup, starting to check periodically ...\n");
1463# ifdef VBOX_WITH_FLTK
1464 Fl::add_timeout(0.5 /* 500 ms */, cb_check_creds, &ctx);
1465# else
1466 g_timeout_add(500 /* ms */, (GSourceFunc)cb_check_creds, &ctx);
1467# endif
1468 }
1469 }
1470#endif /* VBOX_WITH_GUEST_PROPS */
1471
1472#ifdef VBOX_WITH_FLTK
1473 /*
1474 * Do own GDK main loop processing because FLTK also needs
1475 * to have the chance of processing its events.
1476 */
1477 GMainContext *pMainCtx = g_main_context_default();
1478 AssertPtr(pMainCtx);
1479
1480 while (g_fRunning)
1481 {
1482 g_main_context_iteration(pMainCtx,
1483 FALSE /* No blocking */);
1484 Fl::check();
1485 RTThreadSleep(10); /* Wait a bit, don't hog the CPU too much. */
1486 }
1487
1488 g_main_context_unref(pMainCtx);
1489
1490# ifdef VBOX_GREETER_WITH_PNG_SUPPORT
1491 if (pImgBanner)
1492 {
1493 delete pImgBanner; /* Call destructor to free bitmap data. */
1494 pImgBanner = NULL;
1495 }
1496# endif /* VBOX_GREETER_WITH_PNG_SUPPORT */
1497#else /* !VBOX_WITH_FLTK */
1498 gtk_main();
1499 /** @todo Never reached so far. LightDM sends a SIGTERM. */
1500#endif /* !VBOX_WITH_FLTK */
1501
1502 vboxGreeterLog("terminating\n");
1503
1504#ifdef VBOX_WITH_GUEST_PROPS
1505 if (uClientId)
1506 {
1507 rc2 = VbglR3GuestPropDisconnect(uClientId);
1508 AssertRC(rc2);
1509 }
1510#endif /* VBOX_WITH_GUEST_PROPS */
1511
1512 VbglR3Term();
1513
1514 RTEXITCODE rcExit = RT_SUCCESS(rc)
1515 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1516
1517 vboxGreeterLog("terminated with exit code %ld (rc=%Rrc)\n",
1518 rcExit, rc);
1519
1520 vboxGreeterLogDestroy();
1521
1522 return rcExit;
1523}
1524
1525#ifdef DEBUG
1526DECLEXPORT(void) RTAssertMsg1Weak(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction)
1527{
1528 RTAssertMsg1(pszExpr, uLine, pszFile, pszFunction);
1529}
1530#endif
1531
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette