VirtualBox

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

Last change on this file since 56750 was 55905, checked in by vboxsync, 10 years ago

warning

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