VirtualBox

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

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

lightdm-greeter: Proper termination on SIGTERM, improved logging.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 50.6 KB
Line 
1/* $Id: vbox-greeter.cpp 45859 2013-05-01 08:40:06Z 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 rc = VbglR3CredentialsRetrieve(&pszUsername, &pCtx->pszPassword, NULL /* pszDomain */);
375 if (RT_FAILURE(rc))
376 {
377 vboxGreeterError("vboxGreeterCheckCreds: could not retrieve credentials! rc=%Rrc. Aborting\n", rc);
378 }
379 else
380 {
381 vboxGreeterLog("vboxGreeterCheckCreds: credentials retrieved: user=%s, password=%s\n",
382 pszUsername,
383#ifdef DEBUG
384 pCtx->pszPassword);
385#else
386 "XXX");
387#endif
388 /* Trigger LightDM authentication with the user name just retrieved. */
389 lightdm_greeter_authenticate(pCtx->pGreeter, pszUsername); /* Must be the real user name from host! */
390
391 /* Securely wipe the user name again. */
392 VbglR3CredentialsDestroy(pszUsername, NULL /* pszPassword */, NULL /* pszDomain */,
393 3 /* Three wipe passes */);
394 }
395 }
396
397#ifdef DEBUG
398 vboxGreeterLog("vboxGreeterCheckCreds: returned with rc=%Rrc\n", rc);
399#endif
400 return rc;
401}
402
403/**
404 * Called by LightDM when greeter is not needed anymore.
405 *
406 * @param signum Signal number.
407 */
408static void cb_sigterm(int signum)
409{
410 /* Note: This handler must be reentrant-safe. */
411#ifdef VBOX_WITH_FLTK
412 g_fRunning = false;
413#else
414 exit(RTEXITCODE_SUCCESS);
415#endif
416}
417
418/**
419 * Callback for showing a user prompt, issued by the LightDM server.
420 *
421 * @param pGreeter Pointer to this greeter instance.
422 * @param pszText Text to display.
423 * @param enmType Type of prompt to display.
424 * @param pvData Pointer to user-supplied data.
425 */
426static void cb_lightdm_show_prompt(LightDMGreeter *pGreeter,
427 const gchar *pszText, LightDMPromptType enmType,
428 gpointer pvData)
429{
430 vboxGreeterLog("cb_lightdm_show_prompt: text=%s, type=%d\n", pszText, enmType);
431
432 PVBOXGREETERCTX pCtx = (PVBOXGREETERCTX)pvData;
433 AssertPtr(pCtx);
434
435 switch (enmType)
436 {
437 case 1: /* Password. */
438 {
439 if (pCtx->pszPassword)
440 {
441 lightdm_greeter_respond(pGreeter, pCtx->pszPassword);
442 }
443 else
444 {
445#ifdef VBOX_WITH_FLTK
446 AssertPtr(pCtx->pEdtPassword);
447 const char *pszPwd = pCtx->pEdtPassword->value();
448#else
449 GtkEntry *pEdtPwd = GTK_ENTRY(gtk_builder_get_object(pCtx->pBuilder, "edt_password"));
450 AssertPtr(pEdtPwd);
451 const gchar *pszPwd = gtk_entry_get_text(pEdtPwd);
452#endif
453 lightdm_greeter_respond(pGreeter, pszPwd);
454 }
455 break;
456 }
457 /** @todo Other fields? */
458
459 default:
460 break;
461 }
462
463 VbglR3CredentialsDestroy(NULL /* pszUsername */, pCtx->pszPassword, NULL /* pszDomain */,
464 3 /* Three wipe passes */);
465 pCtx->pszPassword = NULL;
466}
467
468/**
469 * Callback for showing a message, issued by the LightDM server.
470 *
471 * @param pGreeter Pointer to this greeter instance.
472 * @param pszText Text to display.
473 * @param enmType Type of message to display.
474 * @param pvData Pointer to user-supplied data.
475 */
476static void cb_lightdm_show_message(LightDMGreeter *pGreeter,
477 const gchar *pszText, LightDMPromptType enmType,
478 gpointer pvData)
479{
480 vboxGreeterLog("cb_lightdm_show_message: text=%s, type=%d\n", pszText, enmType);
481
482 PVBOXGREETERCTX pCtx = (PVBOXGREETERCTX)pvData;
483 AssertPtrReturnVoid(pCtx);
484
485#ifdef VBOX_WITH_FLTK
486 AssertPtr(pCtx->pLblInfo);
487 pCtx->pLblInfo->copy_label(pszText);
488#else
489 GtkLabel *pLblInfo = GTK_LABEL(gtk_builder_get_object(pCtx->pBuilder, "lbl_info"));
490 AssertPtr(pLblInfo);
491 gtk_label_set_text(pLblInfo, pszText);
492#endif
493}
494
495/**
496 * Callback for authentication completion, issued by the LightDM server.
497 *
498 * @param pGreeter Pointer to this greeter instance.
499 */
500static void cb_lightdm_auth_complete(LightDMGreeter *pGreeter)
501{
502 vboxGreeterLog("cb_lightdm_auth_complete\n");
503
504 const gchar *pszUser = lightdm_greeter_get_authentication_user(pGreeter);
505 vboxGreeterLog("authenticating user: %s\n", pszUser ? pszUser : "<NULL>");
506
507 if (lightdm_greeter_get_is_authenticated(pGreeter))
508 {
509 /** @todo Add non-default session support. */
510 gchar *pszSession = g_strdup(lightdm_greeter_get_default_session_hint(pGreeter));
511 if (pszSession)
512 {
513 vboxGreeterLog("starting session: %s\n", pszSession);
514 GError *pError = NULL;
515 if (!lightdm_greeter_start_session_sync(pGreeter, pszSession, &pError))
516 {
517 vboxGreeterError("unable to start session '%s': %s\n",
518 pszSession, pError ? pError->message : "Unknown error");
519 }
520 else
521 {
522 AssertPtr(pszSession);
523 vboxGreeterLog("session '%s' successfully started\n", pszSession);
524 }
525 if (pError)
526 g_error_free(pError);
527 g_free(pszSession);
528 }
529 else
530 vboxGreeterError("unable to get default session\n");
531 }
532 else
533 vboxGreeterLog("user not authenticated successfully (yet)\n");
534}
535
536/**
537 * Callback for clicking on the "Login" button.
538 *
539 * @param pWidget Widget this callback is bound to.
540 * @param pvData Pointer to user-supplied data.
541 */
542#ifdef VBOX_WITH_FLTK
543void cb_btn_login(Fl_Widget *pWidget, void *pvData)
544#else
545void cb_btn_login(GtkWidget *pWidget, gpointer pvData)
546#endif
547{
548 PVBOXGREETERCTX pCtx = (PVBOXGREETERCTX)pvData;
549 AssertPtr(pCtx);
550
551#ifdef VBOX_WITH_FLTK
552 AssertPtr(pCtx->pEdtUsername);
553 const char *pszUser = pCtx->pEdtUsername->value();
554 AssertPtr(pCtx->pEdtPassword);
555 const char *pszPwd = pCtx->pEdtPassword->value();
556#else
557 GtkEntry *pEdtUser = GTK_ENTRY(gtk_builder_get_object(pCtx->pBuilder, VBOX_GREETER_UI_EDT_USER));
558 AssertPtr(pEdtUser);
559 const gchar *pszUser = gtk_entry_get_text(pEdtUser);
560
561 GtkEntry *pEdtPwd = GTK_ENTRY(gtk_builder_get_object(pCtx->pBuilder, VBOX_GREETER_UI_EDT_PASSWORD));
562 AssertPtr(pEdtPwd);
563 const gchar *pszPwd = gtk_entry_get_text(pEdtPwd);
564#endif
565
566 /** @todo Add domain handling? */
567 vboxGreeterLog("login button pressed: greeter=%p, user=%s, password=%s\n",
568 pCtx->pGreeter,
569 pszUser ? pszUser : "<NONE>",
570#ifdef DEBUG
571 pszPwd ? pszPwd : "<NONE>");
572#else
573 /* Don't log passwords in release mode! */
574 "XXX");
575#endif
576 if (strlen(pszUser)) /* Only authenticate if username is given. */
577 {
578 lightdm_greeter_respond(pCtx->pGreeter, pszPwd);
579 lightdm_greeter_authenticate(pCtx->pGreeter, pszUser);
580 }
581}
582
583/**
584 * Callback for clicking on the "Menu" button.
585 *
586 * @param pWidget Widget this callback is bound to.
587 * @param pvData Pointer to user-supplied data.
588 */
589#ifdef VBOX_WITH_FLTK
590void cb_btn_menu(Fl_Widget *pWidget, void *pvData)
591#else
592void cb_btn_menu(GtkWidget *pWidget, gpointer pvData)
593#endif
594{
595 vboxGreeterLog("menu button pressed\n");
596}
597
598/**
599 * Callback for clicking on the "Restart" button / menu entry.
600 *
601 * @param pWidget Widget this callback is bound to.
602 * @param pvData Pointer to user-supplied data.
603 */
604#ifdef VBOX_WITH_FLTK
605void cb_btn_restart(Fl_Widget *pWidget, void *pvData)
606#else
607void cb_btn_restart(GtkWidget *pWidget, gpointer pvData)
608#endif
609{
610 vboxGreeterLog("restart button pressed\n");
611
612 bool fRestart = true;
613#ifdef VBOX_WITH_FLTK
614 int rc = fl_choice("Really restart the system?", "Yes", "No", NULL);
615 fRestart = rc == 0;
616#endif
617
618 if (fRestart)
619 {
620 vboxGreeterLog("restart requested\n");
621#ifndef DEBUG
622 lightdm_restart(NULL);
623#endif
624 }
625}
626
627/**
628 * Callback for clicking on the "Shutdown" button / menu entry.
629 *
630 * @param pWidget Widget this callback is bound to.
631 * @param pvData Pointer to user-supplied data.
632 */
633#ifdef VBOX_WITH_FLTK
634void cb_btn_shutdown(Fl_Widget *pWidget, void *pvData)
635#else
636void cb_btn_shutdown(GtkWidget *pWidget, gpointer pvData)
637#endif
638{
639 vboxGreeterLog("shutdown button pressed\n");
640
641 bool fShutdown = true;
642#ifdef VBOX_WITH_FLTK
643 int rc = fl_choice("Really shutdown the system?", "Yes", "No", NULL);
644 fShutdown = rc == 0;
645#endif
646
647 if (fShutdown)
648 {
649 vboxGreeterLog("shutdown requested\n");
650#ifndef DEBUG
651 lightdm_shutdown(NULL);
652#endif
653 }
654}
655
656#ifdef VBOX_WITH_FLTK
657void cb_edt_username(Fl_Widget *pWidget, void *pvData)
658#else
659void cb_edt_username(GtkWidget *pWidget, gpointer pvData)
660#endif
661{
662 vboxGreeterLog("cb_edt_username called\n");
663
664 PVBOXGREETERCTX pCtx = (PVBOXGREETERCTX)pvData;
665 AssertPtr(pCtx);
666#ifdef VBOX_WITH_FLTK
667 AssertPtr(pCtx->pEdtPassword);
668 Fl::focus(pCtx->pEdtPassword);
669#endif
670}
671
672#ifdef VBOX_WITH_FLTK
673void cb_edt_password(Fl_Widget *pWidget, void *pvData)
674#else
675void cb_edt_password(GtkWidget *pWidget, gpointer pvData)
676#endif
677{
678 vboxGreeterLog("cb_edt_password called\n");
679
680 PVBOXGREETERCTX pCtx = (PVBOXGREETERCTX)pvData;
681 AssertPtr(pCtx);
682#ifdef VBOX_WITH_FLTK
683 AssertPtr(pCtx->pBtnLogin);
684 cb_btn_login(pCtx->pBtnLogin, pvData);
685#endif
686}
687
688/**
689 * Callback for the timer event which is checking for new credentials
690 * from the host.
691 *
692 * @param pvData Pointer to user-supplied data.
693 */
694#ifdef VBOX_WITH_FLTK
695static void cb_check_creds(void *pvData)
696#else
697static gboolean cb_check_creds(gpointer pvData)
698#endif
699{
700 PVBOXGREETERCTX pCtx = (PVBOXGREETERCTX)pvData;
701 AssertPtr(pCtx);
702
703#ifdef DEBUG
704 vboxGreeterLog("cb_check_creds called, clientId=%RU32, timeoutMS=%RU32\n",
705 pCtx->uClientId, pCtx->uTimeoutMS);
706#endif
707
708 int rc = VINF_SUCCESS;
709
710#ifdef VBOX_WITH_GUEST_PROPS
711 bool fAbort = false;
712 char szVal[255];
713 if (pCtx->uClientId)
714 {
715 uint64_t tsAbort;
716 rc = vbox_read_prop(pCtx->uClientId,
717 "/VirtualBox/GuestAdd/PAM/CredsWaitAbort",
718 true /* Read-only on guest */,
719 szVal, sizeof(szVal), &tsAbort);
720 switch (rc)
721 {
722 case VINF_SUCCESS:
723# ifdef DEBUG
724 vboxGreeterLog("cb_check_creds: tsAbort %RU64 <-> %RU64\n",
725 pCtx->uTsAbort, tsAbort);
726# endif
727 if (tsAbort != pCtx->uTsAbort)
728 fAbort = true; /* Timestamps differs, abort. */
729 pCtx->uTsAbort = tsAbort;
730 break;
731
732 case VERR_TOO_MUCH_DATA:
733 vboxGreeterError("cb_check_creds: temporarily unable to get abort notification\n");
734 break;
735
736 case VERR_NOT_FOUND:
737 /* Value not found, continue checking for credentials. */
738 break;
739
740 default:
741 vboxGreeterError("cb_check_creds: the abort notification request failed with rc=%Rrc\n", rc);
742 fAbort = true; /* Abort on error. */
743 break;
744 }
745 }
746
747 if (fAbort)
748 {
749 /* Get optional message. */
750 szVal[0] = '\0';
751 int rc2 = vbox_read_prop(pCtx->uClientId,
752 "/VirtualBox/GuestAdd/PAM/CredsMsgWaitAbort",
753 true /* Read-only on guest */,
754 szVal, sizeof(szVal), NULL /* Timestamp. */);
755 if ( RT_FAILURE(rc2)
756 && rc2 != VERR_NOT_FOUND)
757 vboxGreeterError("cb_check_creds: getting wait abort message failed with rc=%Rrc\n", rc2);
758# ifdef VBOX_WITH_FLTK
759 AssertPtr(pCtx->pLblInfo);
760 pCtx->pLblInfo->copy_label(szVal);
761# else /* !VBOX_WITH_FLTK */
762 GtkLabel *pLblInfo = GTK_LABEL(gtk_builder_get_object(pCtx->pBuilder, VBOX_GREETER_UI_LBL_INFO));
763 AssertPtr(pLblInfo);
764 gtk_label_set_text(pLblInfo, szVal);
765# endif /* !VBOX_WITH_FLTK */
766 vboxGreeterLog("cb_check_creds: got notification from host to abort waiting\n");
767 }
768 else
769 {
770#endif /* VBOX_WITH_GUEST_PROPS */
771 rc = vboxGreeterCheckCreds(pCtx);
772 if (RT_SUCCESS(rc))
773 {
774 /* Credentials retrieved. */
775 }
776 else if (rc == VERR_NOT_FOUND)
777 {
778 /* No credentials found, but try next round (if there's
779 * time left for) ... */
780 }
781#ifdef VBOX_WITH_GUEST_PROPS
782 }
783#endif /* VBOX_WITH_GUEST_PROPS */
784
785 if (rc == VERR_NOT_FOUND) /* No credential found this round. */
786 {
787 /* Calculate timeout value left after process has been started. */
788 uint64_t u64Elapsed = RTTimeMilliTS() - pCtx->uStartMS;
789 /* Is it time to bail out? */
790 if (pCtx->uTimeoutMS < u64Elapsed)
791 {
792#ifdef VBOX_WITH_GUEST_PROPS
793 szVal[0] = '\0';
794 int rc2 = vbox_read_prop(pCtx->uClientId,
795 "/VirtualBox/GuestAdd/PAM/CredsMsgWaitTimeout",
796 true /* Read-only on guest */,
797 szVal, sizeof(szVal), NULL /* Timestamp. */);
798 if ( RT_FAILURE(rc2)
799 && rc2 != VERR_NOT_FOUND)
800 vboxGreeterError("cb_check_creds: getting wait timeout message failed with rc=%Rrc\n", rc2);
801# ifdef VBOX_WITH_FLTK
802 AssertPtr(pCtx->pLblInfo);
803 pCtx->pLblInfo->copy_label(szVal);
804# else
805 GtkLabel *pLblInfo = GTK_LABEL(gtk_builder_get_object(pCtx->pBuilder, VBOX_GREETER_UI_LBL_INFO));
806 AssertPtr(pLblInfo);
807 gtk_label_set_text(pLblInfo, szVal);
808# endif
809#endif /* VBOX_WITH_GUEST_PROPS */
810 vboxGreeterLog("cb_check_creds: no credentials retrieved within time (%RU32ms), giving up\n",
811 pCtx->uTimeoutMS);
812 rc = VERR_TIMEOUT;
813 }
814 }
815
816#ifdef DEBUG
817 vboxGreeterLog("cb_check_creds returned with rc=%Rrc\n", rc);
818#endif
819
820 /* At the moment we only allow *one* shot from the host,
821 * so setting credentials in a second attempt won't be possible
822 * intentionally. */
823
824 if (rc == VERR_NOT_FOUND)
825#ifdef VBOX_WITH_FLTK
826 Fl::repeat_timeout(0.5 /* 500 ms */, cb_check_creds, pvData);
827#else
828 return TRUE; /* No credentials found, do another round. */
829
830 return FALSE; /* Remove timer source on every other error / status. */
831#endif
832}
833
834/**
835 * Release logger callback.
836 *
837 * @return IPRT status code.
838 * @param pLoggerRelease
839 * @param enmPhase
840 * @param pfnLog
841 */
842static void vboxGreeterLogHeaderFooter(PRTLOGGER pLoggerRelease, RTLOGPHASE enmPhase, PFNRTLOGPHASEMSG pfnLog)
843{
844 /* Some introductory information. */
845 static RTTIMESPEC s_TimeSpec;
846 char szTmp[256];
847 if (enmPhase == RTLOGPHASE_BEGIN)
848 RTTimeNow(&s_TimeSpec);
849 RTTimeSpecToString(&s_TimeSpec, szTmp, sizeof(szTmp));
850
851 switch (enmPhase)
852 {
853 case RTLOGPHASE_BEGIN:
854 {
855 pfnLog(pLoggerRelease,
856 "vbox-greeter %s r%s (verbosity: %d) %s (%s %s) release log\n"
857 "Log opened %s\n",
858 RTBldCfgVersion(), RTBldCfgRevisionStr(), g_iVerbosity, VBOX_BUILD_TARGET,
859 __DATE__, __TIME__, szTmp);
860
861 int vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
862 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
863 pfnLog(pLoggerRelease, "OS Product: %s\n", szTmp);
864 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
865 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
866 pfnLog(pLoggerRelease, "OS Release: %s\n", szTmp);
867 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
868 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
869 pfnLog(pLoggerRelease, "OS Version: %s\n", szTmp);
870 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
871 pfnLog(pLoggerRelease, "OS Service Pack: %s\n", szTmp);
872
873 /* the package type is interesting for Linux distributions */
874 char szExecName[RTPATH_MAX];
875 char *pszExecName = RTProcGetExecutablePath(szExecName, sizeof(szExecName));
876 pfnLog(pLoggerRelease,
877 "Executable: %s\n"
878 "Process ID: %u\n"
879 "Package type: %s"
880#ifdef VBOX_OSE
881 " (OSE)"
882#endif
883 "\n",
884 pszExecName ? pszExecName : "unknown",
885 RTProcSelf(),
886 VBOX_PACKAGE_STRING);
887 break;
888 }
889
890 case RTLOGPHASE_PREROTATE:
891 pfnLog(pLoggerRelease, "Log rotated - Log started %s\n", szTmp);
892 break;
893
894 case RTLOGPHASE_POSTROTATE:
895 pfnLog(pLoggerRelease, "Log continuation - Log started %s\n", szTmp);
896 break;
897
898 case RTLOGPHASE_END:
899 pfnLog(pLoggerRelease, "End of log file - Log started %s\n", szTmp);
900 break;
901
902 default:
903 /* nothing */;
904 }
905}
906
907/**
908 * Creates the default release logger outputting to the specified file.
909 *
910 * @return IPRT status code.
911 * @param pszLogFile Filename for log output. Optional.
912 */
913static int vboxGreeterLogCreate(const char *pszLogFile)
914{
915 /* Create release logger (stdout + file). */
916 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
917 RTUINT fFlags = RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG;
918#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
919 fFlags |= RTLOGFLAGS_USECRLF;
920#endif
921 char szError[RTPATH_MAX + 128] = "";
922 int rc = RTLogCreateEx(&g_pLoggerRelease, fFlags, "all",
923 "VBOXGREETER_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups,
924 RTLOGDEST_STDOUT,
925 vboxGreeterLogHeaderFooter, g_cHistory, g_uHistoryFileSize, g_uHistoryFileTime,
926 szError, sizeof(szError), pszLogFile);
927 if (RT_SUCCESS(rc))
928 {
929 /* register this logger as the release logger */
930 RTLogRelSetDefaultInstance(g_pLoggerRelease);
931
932 /* Explicitly flush the log in case of VBOXGREETER_RELEASE_LOG_FLAGS=buffered. */
933 RTLogFlush(g_pLoggerRelease);
934 }
935
936 return rc;
937}
938
939static void vboxGreeterLogDestroy(void)
940{
941 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
942}
943
944static int vboxGreeterUsage(void)
945{
946 RTPrintf("Usage:\n"
947 " %-12s [-h|-?|--help] [-F|--logfile <file>]\n"
948 " [-v|--verbose] [-V|--version]\n", g_pszProgName);
949
950 RTPrintf("\n"
951 " Copyright (C) 2012-" VBOX_C_YEAR " " VBOX_VENDOR "\n");
952
953 return RTEXITCODE_SYNTAX;
954}
955
956int main(int argc, char **argv)
957{
958 int rc = RTR3InitExe(argc, &argv, 0);
959 if (RT_FAILURE(rc))
960 return RTMsgInitFailure(rc);
961 g_pszProgName = RTPathFilename(argv[0]);
962
963 static const RTGETOPTDEF s_aOptions[] =
964 {
965 { "--logfile", 'F', RTGETOPT_REQ_STRING },
966 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
967 { "--version", 'V', RTGETOPT_REQ_NOTHING }
968 };
969
970 char szLogFile[RTPATH_MAX + 128] = "";
971
972 int ch;
973 RTGETOPTUNION ValueUnion;
974 RTGETOPTSTATE GetState;
975 RTGetOptInit(&GetState, argc, argv,
976 s_aOptions, RT_ELEMENTS(s_aOptions),
977 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
978
979 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
980 && RT_SUCCESS(rc))
981 {
982 /* For options that require an argument, ValueUnion has received the value. */
983 switch (ch)
984 {
985 case 'F':
986 if (!RTStrPrintf(szLogFile, sizeof(szLogFile), "%s", ValueUnion.psz))
987 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to get prepare log file name");
988 break;
989
990 case 'h':
991 case '?':
992 return vboxGreeterUsage();
993
994 case 'v': /* Raise verbosity. */
995 g_iVerbosity++;
996 break;
997
998 case 'V': /* Print version and exit. */
999 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
1000 return RTEXITCODE_SUCCESS;
1001 break; /* Never reached. */
1002
1003 default:
1004 return RTGetOptPrintError(ch, &ValueUnion);
1005 }
1006 }
1007
1008 if (RT_FAILURE(rc))
1009 return RTEXITCODE_SYNTAX;
1010
1011 rc = VbglR3InitUser();
1012 if (RT_FAILURE(rc))
1013 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to init Vbgl (%Rrc)", rc);
1014
1015 rc = vboxGreeterLogCreate(strlen(szLogFile) ? szLogFile : NULL);
1016 if (RT_FAILURE(rc))
1017 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create release log (%s, %Rrc)",
1018 strlen(szLogFile) ? szLogFile : "<None>", rc);
1019
1020 vboxGreeterLog("init\n");
1021
1022 signal(SIGTERM, cb_sigterm);
1023
1024 /** @todo This function already is too long. Move code into
1025 * functions. */
1026
1027 VBOXGREETERCTX ctx;
1028 RT_ZERO(ctx);
1029
1030 /* UI parameters. */
1031 uint32_t uBgColor = 0; /* The background color. */
1032 uint32_t uLogonDlgHdrColor = 0;
1033 uint32_t uLogonDlgBgColor = 0; /* The greeter's dialog color. */
1034 uint32_t uLogonDlgBtnColor = 0; /* The greeter's button color. */
1035
1036 char szBannerPath[RTPATH_MAX];
1037
1038 /* By default most UI elements are shown. */
1039 uint32_t uOptsUI = VBOX_GREETER_UI_SHOW_RESTART
1040 | VBOX_GREETER_UI_SHOW_SHUTDOWN;
1041#ifdef VBOX_WITH_GUEST_PROPS
1042 uint32_t uClientId = 0;
1043 rc = VbglR3GuestPropConnect(&uClientId);
1044 if (RT_SUCCESS(rc))
1045 {
1046 vboxGreeterLog("clientId=%RU32\n", uClientId);
1047
1048 ctx.uClientId = uClientId;
1049
1050 char szVal[256];
1051 int rc2 = vbox_read_prop(uClientId,
1052 "/VirtualBox/GuestAdd/Greeter/HideRestart",
1053 true /* Read-only on guest */,
1054 szVal, sizeof(szVal), NULL /* Timestamp. */);
1055 if ( RT_SUCCESS(rc2)
1056 && !RTStrICmp(szVal, "1"))
1057 {
1058 uOptsUI &= ~VBOX_GREETER_UI_SHOW_RESTART;
1059 }
1060
1061 rc2 = vbox_read_prop(uClientId,
1062 "/VirtualBox/GuestAdd/Greeter/HideShutdown",
1063 true /* Read-only on guest */,
1064 szVal, sizeof(szVal), NULL /* Timestamp. */);
1065 if ( RT_SUCCESS(rc2)
1066 && !RTStrICmp(szVal, "1"))
1067 {
1068 uOptsUI &= ~VBOX_GREETER_UI_SHOW_SHUTDOWN;
1069 }
1070
1071# ifdef VBOX_GREETER_WITH_PNG_SUPPORT
1072 /* Load the banner. */
1073 rc2 = vbox_read_prop(uClientId,
1074 "/VirtualBox/GuestAdd/Greeter/BannerPath",
1075 true /* Read-only on guest */,
1076 szBannerPath, sizeof(szBannerPath), NULL /* Timestamp. */);
1077 if (RT_SUCCESS(rc2))
1078 {
1079 if (RTFileExists(szBannerPath))
1080 {
1081 vboxGreeterLog("showing banner from '%s'\n", szBannerPath);
1082 uOptsUI |= VBOX_GREETER_UI_SHOW_BANNER;
1083 }
1084 else
1085 vboxGreeterLog("warning: unable to find banner at '%s', skipping\n", szBannerPath);
1086 }
1087# endif /* VBOX_GREETER_WITH_PNG_SUPPORT */
1088
1089 /* Use theming?. */
1090 rc2 = vbox_read_prop(uClientId,
1091 "/VirtualBox/GuestAdd/Greeter/UseTheming",
1092 true /* Read-only on guest */,
1093 szVal, sizeof(szVal), NULL /* Timestamp. */);
1094 if ( RT_SUCCESS(rc2)
1095 && !RTStrICmp(szVal, "1"))
1096 {
1097 vboxGreeterLog("custom theming enabled\n");
1098 uOptsUI |= VBOX_GREETER_UI_USE_THEMING;
1099 }
1100
1101 if (uOptsUI & VBOX_GREETER_UI_USE_THEMING)
1102 {
1103 /* Get background color. */
1104 rc2 = vbox_read_prop(uClientId,
1105 "/VirtualBox/GuestAdd/Greeter/Theme/BackgroundColor",
1106 true /* Read-only on guest */,
1107 szVal, sizeof(szVal), NULL /* Timestamp. */);
1108 if (RT_SUCCESS(rc2))
1109 {
1110 uBgColor = strtol(szVal, NULL,
1111 /* Change conversion base when having a 0x prefix. */
1112 RTStrStr(szVal, "0x") == szVal ? 0 : 16);
1113 }
1114
1115 /* Logon dialog. */
1116
1117 /* Get header color. */
1118 rc2 = vbox_read_prop(uClientId,
1119 "/VirtualBox/GuestAdd/Greeter/Theme/LogonDialog/HeaderColor",
1120 true /* Read-only on guest */,
1121 szVal, sizeof(szVal), NULL /* Timestamp. */);
1122 if (RT_SUCCESS(rc2))
1123 {
1124 uLogonDlgHdrColor = strtol(szVal, NULL,
1125 /* Change conversion base when having a 0x prefix. */
1126 RTStrStr(szVal, "0x") == szVal ? 0 : 16);
1127 }
1128
1129 /* Get dialog color. */
1130 rc2 = vbox_read_prop(uClientId,
1131 "/VirtualBox/GuestAdd/Greeter/Theme/LogonDialog/BackgroundColor",
1132 true /* Read-only on guest */,
1133 szVal, sizeof(szVal), NULL /* Timestamp. */);
1134 if (RT_SUCCESS(rc2))
1135 {
1136 uLogonDlgBgColor = strtol(szVal, NULL,
1137 /* Change conversion base when having a 0x prefix. */
1138 RTStrStr(szVal, "0x") == szVal ? 0 : 16);
1139 }
1140
1141 /* Get button color. */
1142 rc2 = vbox_read_prop(uClientId,
1143 "/VirtualBox/GuestAdd/Greeter/Theme/LogonDialog/ButtonColor",
1144 true /* Read-only on guest */,
1145 szVal, sizeof(szVal), NULL /* Timestamp. */);
1146 if (RT_SUCCESS(rc2))
1147 {
1148 uLogonDlgBtnColor = strtol(szVal, NULL,
1149 /* Change conversion base when having a 0x prefix. */
1150 RTStrStr(szVal, "0x") == szVal ? 0 : 16);
1151 }
1152 }
1153 }
1154 else
1155 vboxGreeterError("unable to connect to guest property service, rc=%Rrc\n", rc);
1156#endif
1157 vboxGreeterLog("UI options are: %RU32\n", uOptsUI);
1158
1159#ifdef VBOX_WITH_FLTK
1160 int rc2 = Fl::scheme("plastic");
1161 if (!rc2)
1162 vboxGreeterLog("warning: unable to set visual scheme\n");
1163
1164 Fl::visual(FL_DOUBLE | FL_INDEX);
1165 Fl_Double_Window *pWndMain = new Fl_Double_Window(Fl::w(), Fl::h(), "VirtualBox Guest Additions");
1166 AssertPtr(pWndMain);
1167 if (uOptsUI & VBOX_GREETER_UI_USE_THEMING)
1168 pWndMain->color(fl_rgb_color(VBOX_RGB_COLOR_RED(uBgColor),
1169 VBOX_RGB_COLOR_GREEN(uBgColor),
1170 VBOX_RGB_COLOR_BLUE(uBgColor)));
1171 else /* Default colors. */
1172 pWndMain->color(fl_rgb_color(0x73, 0x7F, 0x8C));
1173
1174 Fl_Double_Window *pWndGreeter = new Fl_Double_Window(500, 350);
1175 AssertPtr(pWndGreeter);
1176 pWndGreeter->set_modal();
1177 if (uOptsUI & VBOX_GREETER_UI_USE_THEMING)
1178 pWndGreeter->color(fl_rgb_color(VBOX_RGB_COLOR_RED(uLogonDlgBgColor),
1179 VBOX_RGB_COLOR_GREEN(uLogonDlgBgColor),
1180 VBOX_RGB_COLOR_BLUE(uLogonDlgBgColor)));
1181 else /* Default colors. */
1182 pWndGreeter->color(fl_rgb_color(255, 255, 255));
1183
1184 uint32_t uOffsetX = 130;
1185 /**
1186 * For now we're using a simple Y offset for moving all elements
1187 * down if a banner needs to be shown on top of the greeter. Not
1188 * very clean but does the job. Use some more layouting stuff
1189 * when this gets more complex.
1190 */
1191 uint32_t uOffsetY = 80;
1192
1193# ifdef VBOX_GREETER_WITH_PNG_SUPPORT
1194 fl_register_images();
1195
1196 /** @todo Add basic image type detection based on file
1197 * extension. */
1198
1199 Fl_PNG_Image *pImgBanner = NULL;
1200 if (uOptsUI & VBOX_GREETER_UI_SHOW_BANNER)
1201 {
1202 pImgBanner = new Fl_PNG_Image(szBannerPath);
1203 AssertPtr(pImgBanner);
1204
1205 /** @todo Make the banner size configurable via guest
1206 * properties. For now it's hardcoded to 460 x 90px. */
1207 Fl_Box *pBoxBanner = new Fl_Box(20, uOffsetY, 460, 90, "");
1208 AssertPtr(pBoxBanner);
1209 pBoxBanner->image(pImgBanner);
1210
1211 uOffsetY = 120;
1212 }
1213# endif
1214
1215 Fl_Box *pLblHeader = new Fl_Box(FL_NO_BOX, 242, uOffsetY, 300, 20,
1216 "Desktop Login");
1217 AssertPtr(pLblHeader);
1218
1219 /** Note to use an own font:
1220 * Fl_Font myfnt = FL_FREE_FONT + 1;
1221 * Fl::set_font(myfnt, "MyFont"); */
1222 Fl_Font fntHeader = FL_FREE_FONT;
1223 Fl::set_font(fntHeader, "Courier");
1224
1225 pLblHeader->align(FL_ALIGN_LEFT);
1226 pLblHeader->labelfont(FL_BOLD);
1227 pLblHeader->labelsize(24);
1228 if (uOptsUI & VBOX_GREETER_UI_USE_THEMING)
1229 pLblHeader->labelcolor(fl_rgb_color(VBOX_RGB_COLOR_RED(uLogonDlgHdrColor),
1230 VBOX_RGB_COLOR_GREEN(uLogonDlgHdrColor),
1231 VBOX_RGB_COLOR_BLUE(uLogonDlgHdrColor)));
1232 else /* Default color. */
1233 pLblHeader->labelcolor(fl_rgb_color(0x51, 0x5F, 0x77));
1234 uOffsetY += 40;
1235
1236 /** @todo Add basic NLS support. */
1237
1238 Fl_Input *pEdtUsername = new Fl_Input(uOffsetX, uOffsetY,
1239 300, 20, "User Name");
1240 AssertPtr(pEdtUsername);
1241 pEdtUsername->callback(cb_edt_username, &ctx);
1242 pEdtUsername->when(FL_WHEN_ENTER_KEY_ALWAYS);
1243 Fl::focus(pEdtUsername);
1244 ctx.pEdtUsername = pEdtUsername;
1245
1246 Fl_Secret_Input *pEdtPassword = new Fl_Secret_Input(uOffsetX, uOffsetY + 40,
1247 300, 20, "Password");
1248 AssertPtr(pEdtPassword);
1249 pEdtPassword->callback(cb_edt_password, &ctx);
1250 pEdtPassword->when(FL_WHEN_ENTER_KEY_ALWAYS);
1251 ctx.pEdtPassword = pEdtPassword;
1252
1253 Fl_Button *pBtnLogin = new Fl_Button(uOffsetX, uOffsetY + 70,
1254 100, 40, "Log In");
1255 AssertPtr(pBtnLogin);
1256 pBtnLogin->callback(cb_btn_login, &ctx);
1257 if (uOptsUI & VBOX_GREETER_UI_USE_THEMING)
1258 pBtnLogin->color(fl_rgb_color(VBOX_RGB_COLOR_RED(uLogonDlgBtnColor),
1259 VBOX_RGB_COLOR_GREEN(uLogonDlgBtnColor),
1260 VBOX_RGB_COLOR_BLUE(uLogonDlgBtnColor)));
1261 else /* Default color. */
1262 pBtnLogin->color(fl_rgb_color(255, 255, 255));
1263 ctx.pBtnLogin = pBtnLogin;
1264
1265 Fl_Menu_Button *pBtnMenu = new Fl_Menu_Button(uOffsetX + 120, uOffsetY + 70,
1266 100, 40, "Options");
1267 AssertPtr(pBtnMenu);
1268 pBtnMenu->callback(cb_btn_menu, &ctx);
1269 if (uOptsUI & VBOX_GREETER_UI_USE_THEMING)
1270 pBtnMenu->color(fl_rgb_color(VBOX_RGB_COLOR_RED(uLogonDlgBtnColor),
1271 VBOX_RGB_COLOR_GREEN(uLogonDlgBtnColor),
1272 VBOX_RGB_COLOR_BLUE(uLogonDlgBtnColor)));
1273 else /* Default color. */
1274 pBtnMenu->color(fl_rgb_color(255, 255, 255));
1275
1276 if (uOptsUI & VBOX_GREETER_UI_SHOW_RESTART)
1277 pBtnMenu->add("Restart", "" /* Shortcut */, cb_btn_restart, &ctx, 0 /* Flags */);
1278 if (uOptsUI & VBOX_GREETER_UI_SHOW_SHUTDOWN)
1279 pBtnMenu->add("Shutdown", "" /* Shortcut */, cb_btn_shutdown, &ctx, 0 /* Flags */);
1280
1281 char szLabel[255];
1282 RTStrPrintf(szLabel, sizeof(szLabel), "Oracle VM VirtualBox Guest Additions %sr%s",
1283 RTBldCfgVersion(), RTBldCfgRevisionStr());
1284 Fl_Box *pLblInfo = new Fl_Box(FL_NO_BOX , 50, uOffsetY + 150,
1285 400, 20, szLabel);
1286 AssertPtr(pLblInfo);
1287 ctx.pLblInfo = pLblInfo;
1288
1289 pWndGreeter->end();
1290 pWndGreeter->position((Fl::w() - pWndGreeter->w()) / 2,
1291 (Fl::h() - pWndGreeter->h()) / 2);
1292
1293 pWndMain->fullscreen();
1294 pWndMain->show(argc, argv);
1295 pWndMain->end();
1296
1297 pWndGreeter->show();
1298#else /* !VBOX_WITH_FLTK */
1299 gtk_init(&argc, &argv);
1300
1301 /* Set default cursor */
1302 gdk_window_set_cursor(gdk_get_default_root_window(), gdk_cursor_new(GDK_LEFT_PTR));
1303
1304 GError *pError = NULL;
1305 GtkBuilder *pBuilder = gtk_builder_new();
1306 AssertPtr(pBuilder);
1307 if (!gtk_builder_add_from_file(pBuilder, "/usr/share/xgreeters/vbox-greeter.ui", &pError))
1308 {
1309 AssertPtr(pError);
1310 vboxGreeterError("unable to load UI: %s", pError->message);
1311 return RTEXITCODE_FAILURE;
1312 }
1313
1314 GtkWindow *pWndGreeter = GTK_WINDOW(gtk_builder_get_object(pBuilder, VBOX_GREETER_UI_WND_GREETER));
1315 AssertPtr(pWndGreeter);
1316 GtkButton *pBtnLogin = GTK_BUTTON(gtk_builder_get_object(pBuilder, VBOX_GREETER_UI_BTN_LOGIN));
1317 AssertPtr(pBtnLogin);
1318 GtkLabel *pLblInfo = GTK_LABEL(gtk_builder_get_object(pBuilder, VBOX_GREETER_UI_LBL_INFO));
1319 AssertPtr(pLblInfo);
1320
1321 ctx.pBuilder = pBuilder;
1322
1323 g_signal_connect(G_OBJECT(pBtnLogin), "clicked", G_CALLBACK(cb_btn_login), &ctx);
1324
1325 GdkRectangle rectScreen;
1326 gdk_screen_get_monitor_geometry(gdk_screen_get_default(), gdk_screen_get_primary_monitor(gdk_screen_get_default()), &rectScreen);
1327 vboxGreeterLog("monitor (default) is %dx%d\n", rectScreen.width, rectScreen.height);
1328
1329 gint iWndX, iWndY;
1330 gtk_window_get_default_size(pWndGreeter, &iWndX, &iWndY);
1331 vboxGreeterLog("greeter is %dx%d\n", iWndX, iWndY);
1332
1333 gtk_window_move(pWndGreeter,
1334 (rectScreen.width / 2) - (iWndX / 2),
1335 (rectScreen.height / 2) - (iWndY / 2));
1336 gtk_widget_show(GTK_WIDGET(pWndGreeter));
1337
1338 g_clear_error(&pError);
1339#endif /* !VBOX_WITH_FLTK */
1340
1341 /* GType is needed in any case (for LightDM), whether we
1342 * use GTK3 or not. */
1343 g_type_init();
1344
1345 GMainLoop *pMainLoop = g_main_loop_new(NULL, FALSE /* Not yet running */);
1346 AssertPtr(pMainLoop);
1347
1348 LightDMGreeter *pGreeter = lightdm_greeter_new();
1349 AssertPtr(pGreeter);
1350
1351 g_signal_connect(pGreeter, "show-prompt", G_CALLBACK(cb_lightdm_show_prompt), &ctx);
1352 g_signal_connect(pGreeter, "show-message", G_CALLBACK(cb_lightdm_show_message), &ctx);
1353 g_signal_connect(pGreeter, "authentication-complete", G_CALLBACK(cb_lightdm_auth_complete), &ctx);
1354
1355 ctx.pGreeter = pGreeter;
1356
1357 if (!lightdm_greeter_connect_sync(pGreeter, NULL))
1358 {
1359 vboxGreeterError("unable to connect to LightDM server, aborting\n");
1360 return RTEXITCODE_FAILURE;
1361 }
1362
1363 vboxGreeterLog("connected to LightDM server\n");
1364
1365#ifdef VBOX_WITH_GUEST_PROPS
1366 bool fCheckCreds = false;
1367 if (uClientId) /* Connected to guest property service? */
1368 {
1369 char szVal[256];
1370 rc2 = vbox_read_prop(uClientId,
1371 "/VirtualBox/GuestAdd/PAM/CredsWait",
1372 true /* Read-only on guest */,
1373 szVal, sizeof(szVal), NULL /* Timestamp. */);
1374 if (RT_SUCCESS(rc2))
1375 {
1376 uint32_t uTimeoutMS = RT_INDEFINITE_WAIT; /* Wait infinite by default. */
1377 rc2 = vbox_read_prop(uClientId,
1378 "/VirtualBox/GuestAdd/PAM/CredsWaitTimeout",
1379 true /* Read-only on guest */,
1380 szVal, sizeof(szVal), NULL /* Timestamp. */);
1381 if (RT_SUCCESS(rc2))
1382 {
1383 uTimeoutMS = RTStrToUInt32(szVal);
1384 if (!uTimeoutMS)
1385 {
1386 vboxGreeterError("pam_vbox_authenticate: invalid waiting timeout value specified, defaulting to infinite timeout\n");
1387 uTimeoutMS = RT_INDEFINITE_WAIT;
1388 }
1389 else
1390 uTimeoutMS = uTimeoutMS * 1000; /* Make ms out of s. */
1391 }
1392
1393 ctx.uTimeoutMS = uTimeoutMS;
1394
1395 rc2 = vbox_read_prop(uClientId,
1396 "/VirtualBox/GuestAdd/PAM/CredsMsgWaiting",
1397 true /* Read-only on guest */,
1398 szVal, sizeof(szVal), NULL /* Timestamp. */);
1399 if (RT_SUCCESS(rc2))
1400 {
1401# ifdef VBOX_WITH_FLTK
1402 Assert(pLblInfo);
1403 pLblInfo->copy_label(szVal);
1404# else
1405 gtk_label_set_text(pLblInfo, szVal);
1406# endif
1407 }
1408
1409 /* Get initial timestamp so that we can compare the time
1410 * whether the value has been changed or not in our event callback. */
1411 vbox_read_prop(uClientId,
1412 "/VirtualBox/GuestAdd/PAM/CredsWaitAbort",
1413 true /* Read-only on guest */,
1414 szVal, sizeof(szVal), &ctx.uTsAbort);
1415
1416 if (RT_SUCCESS(rc))
1417 {
1418 /* Before we actuall wait for credentials just make sure we didn't already get credentials
1419 * set so that we can skip waiting for them ... */
1420 rc2 = vboxGreeterCheckCreds(&ctx);
1421 if (rc2 == VERR_NOT_FOUND)
1422 {
1423 /* Get current time stamp to later calculate rest of timeout left. */
1424 ctx.uStartMS = RTTimeMilliTS();
1425
1426 fCheckCreds = true;
1427 }
1428 }
1429 }
1430
1431 /* Start the timer to check credentials availability. */
1432 if (fCheckCreds)
1433 {
1434 vboxGreeterLog("No credentials available on startup, starting to check periodically ...\n");
1435# ifdef VBOX_WITH_FLTK
1436 Fl::add_timeout(0.5 /* 500 ms */, cb_check_creds, &ctx);
1437# else
1438 g_timeout_add(500 /* ms */, (GSourceFunc)cb_check_creds, &ctx);
1439# endif
1440 }
1441 }
1442#endif /* VBOX_WITH_GUEST_PROPS */
1443
1444#ifdef VBOX_WITH_FLTK
1445 /*
1446 * Do own GDK main loop processing because FLTK also needs
1447 * to have the chance of processing its events.
1448 */
1449 GMainContext *pMainCtx = g_main_context_default();
1450 AssertPtr(pMainCtx);
1451
1452 while (g_fRunning)
1453 {
1454 g_main_context_iteration(pMainCtx,
1455 FALSE /* No blocking */);
1456 Fl::check();
1457 RTThreadYield();
1458 }
1459
1460 g_main_context_unref(pMainCtx);
1461
1462# ifdef VBOX_GREETER_WITH_PNG_SUPPORT
1463 if (pImgBanner)
1464 {
1465 delete pImgBanner; /* Call destructor to free bitmap data. */
1466 pImgBanner = NULL;
1467 }
1468# endif /* VBOX_GREETER_WITH_PNG_SUPPORT */
1469#else /* !VBOX_WITH_FLTK */
1470 gtk_main();
1471 /** @todo Never reached so far. LightDM sends a SIGTERM. */
1472#endif /* !VBOX_WITH_FLTK */
1473
1474 vboxGreeterLog("terminating\n");
1475
1476#ifdef VBOX_WITH_GUEST_PROPS
1477 if (uClientId)
1478 {
1479 rc2 = VbglR3GuestPropDisconnect(uClientId);
1480 AssertRC(rc2);
1481 }
1482#endif /* VBOX_WITH_GUEST_PROPS */
1483
1484 VbglR3Term();
1485
1486 RTEXITCODE rcExit = RT_SUCCESS(rc)
1487 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1488
1489 vboxGreeterLog("terminated with exit code %ld (rc=%Rrc)\n",
1490 rcExit, rc);
1491
1492 vboxGreeterLogDestroy();
1493
1494 return rcExit;
1495}
1496
1497#ifdef DEBUG
1498DECLEXPORT(void) RTAssertMsg1Weak(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction)
1499{
1500 RTAssertMsg1(pszExpr, uLine, pszFile, pszFunction);
1501}
1502#endif
1503
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