VirtualBox

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

Last change on this file since 45521 was 45521, checked in by vboxsync, 12 years ago

lightdm-greeter: Fixed information label drawing, fixed memory leak, todos.

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