VirtualBox

source: vbox/trunk/src/VBox/Main/webservice/vboxweb.cpp@ 25275

Last change on this file since 25275 was 22911, checked in by vboxsync, 15 years ago

event queues cleaned up

  • Property filesplitter.c set to Makefile.kmk
  • Property svn:eol-style set to native
File size: 42.5 KB
Line 
1/**
2 * vboxweb.cpp:
3 * hand-coded parts of the webservice server. This is linked with the
4 * generated code in out/.../src/VBox/Main/webservice/methodmaps.cpp
5 * (plus static gSOAP server code) to implement the actual webservice
6 * server, to which clients can connect.
7 *
8 * Copyright (C) 2006-2009 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23// shared webservice header
24#include "vboxweb.h"
25
26// vbox headers
27#include <VBox/com/com.h>
28#include <VBox/com/ErrorInfo.h>
29#include <VBox/com/errorprint.h>
30#include <VBox/com/EventQueue.h>
31#include <VBox/VRDPAuth.h>
32#include <VBox/version.h>
33
34#include <iprt/lock.h>
35#include <iprt/rand.h>
36#include <iprt/initterm.h>
37#include <iprt/getopt.h>
38#include <iprt/ctype.h>
39#include <iprt/process.h>
40#include <iprt/string.h>
41#include <iprt/ldr.h>
42
43// workaround for compile problems on gcc 4.1
44#ifdef __GNUC__
45#pragma GCC visibility push(default)
46#endif
47
48// gSOAP headers (must come after vbox includes because it checks for conflicting defs)
49#include "soapH.h"
50
51// standard headers
52#include <map>
53
54#ifdef __GNUC__
55#pragma GCC visibility pop
56#endif
57
58// include generated namespaces table
59#include "vboxwebsrv.nsmap"
60
61/****************************************************************************
62 *
63 * private typedefs
64 *
65 ****************************************************************************/
66
67typedef std::map<uint64_t, ManagedObjectRef*>
68 ManagedObjectsMapById;
69typedef std::map<uint64_t, ManagedObjectRef*>::iterator
70 ManagedObjectsIteratorById;
71typedef std::map<uintptr_t, ManagedObjectRef*>
72 ManagedObjectsMapByPtr;
73
74typedef std::map<uint64_t, WebServiceSession*>
75 SessionsMap;
76typedef std::map<uint64_t, WebServiceSession*>::iterator
77 SessionsMapIterator;
78
79int fntWatchdog(RTTHREAD ThreadSelf, void *pvUser);
80
81/****************************************************************************
82 *
83 * Read-only global variables
84 *
85 ****************************************************************************/
86
87ComPtr<IVirtualBox> g_pVirtualBox = NULL;
88
89// generated strings in methodmaps.cpp
90extern const char *g_pcszISession,
91 *g_pcszIVirtualBox;
92
93// globals for vboxweb command-line arguments
94#define DEFAULT_TIMEOUT_SECS 300
95#define DEFAULT_TIMEOUT_SECS_STRING "300"
96int g_iWatchdogTimeoutSecs = DEFAULT_TIMEOUT_SECS;
97int g_iWatchdogCheckInterval = 5;
98
99const char *g_pcszBindToHost = NULL; // host; NULL = current machine
100unsigned int g_uBindToPort = 18083; // port
101unsigned int g_uBacklog = 100; // backlog = max queue size for requests
102
103bool g_fVerbose = false; // be verbose
104PRTSTREAM g_pstrLog = NULL;
105
106#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
107bool g_fDaemonize = false; // run in background.
108#endif
109
110/****************************************************************************
111 *
112 * Writeable global variables
113 *
114 ****************************************************************************/
115
116RTLockMtx g_mutexAuthLib;
117
118// this mutex protects all of the below
119RTLockMtx g_mutexSessions;
120
121SessionsMap g_mapSessions;
122ULONG64 g_iMaxManagedObjectID = 0;
123ULONG64 g_cManagedObjects = 0;
124
125/****************************************************************************
126 *
127 * main
128 *
129 ****************************************************************************/
130
131static const RTGETOPTDEF g_aOptions[]
132 = {
133 { "--help", 'h', RTGETOPT_REQ_NOTHING },
134#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
135 { "--background", 'b', RTGETOPT_REQ_NOTHING },
136#endif
137 { "--host", 'H', RTGETOPT_REQ_STRING },
138 { "--port", 'p', RTGETOPT_REQ_UINT32 },
139 { "--timeout", 't', RTGETOPT_REQ_UINT32 },
140 { "--check-interval", 'i', RTGETOPT_REQ_UINT32 },
141 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
142 { "--logfile", 'F', RTGETOPT_REQ_STRING },
143 };
144
145void DisplayHelp()
146{
147 RTStrmPrintf(g_pStdErr, "\nUsage: vboxwebsrv [options]\n\nSupported options (default values in brackets):\n");
148 for (unsigned i = 0;
149 i < RT_ELEMENTS(g_aOptions);
150 ++i)
151 {
152 std::string str(g_aOptions[i].pszLong);
153 str += ", -";
154 str += g_aOptions[i].iShort;
155 str += ":";
156
157 const char *pcszDescr = "";
158
159 switch (g_aOptions[i].iShort)
160 {
161 case 'h':
162 pcszDescr = "Print this help message and exit.";
163 break;
164
165#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
166 case 'b':
167 pcszDescr = "Run in background (daemon mode).";
168 break;
169#endif
170
171 case 'H':
172 pcszDescr = "The host to bind to (localhost).";
173 break;
174
175 case 'p':
176 pcszDescr = "The port to bind to (18083).";
177 break;
178
179 case 't':
180 pcszDescr = "Session timeout in seconds; 0 = disable timeouts (" DEFAULT_TIMEOUT_SECS_STRING ").";
181 break;
182
183 case 'i':
184 pcszDescr = "Frequency of timeout checks in seconds (5).";
185 break;
186
187 case 'v':
188 pcszDescr = "Be verbose.";
189 break;
190
191 case 'F':
192 pcszDescr = "Name of file to write log to (no file).";
193 break;
194 }
195
196 RTStrmPrintf(g_pStdErr, "%-23s%s\n", str.c_str(), pcszDescr);
197 }
198}
199
200/**
201 * Implementation for WEBLOG macro defined in vboxweb.h; this prints a message
202 * to the console and optionally to the file that may have been given to the
203 * vboxwebsrv command line.
204 * @param pszFormat
205 */
206void WebLog(const char *pszFormat, ...)
207{
208 va_list args;
209 va_start(args, pszFormat);
210 char *psz = NULL;
211 RTStrAPrintfV(&psz, pszFormat, args);
212 va_end(args);
213
214 // terminal
215 RTPrintf("%s", psz);
216
217 // log file
218 if (g_pstrLog)
219 {
220 RTStrmPrintf(g_pstrLog, "%s", psz);
221 RTStrmFlush(g_pstrLog);
222 }
223
224 // logger instance
225 RTLogLoggerEx(LOG_INSTANCE, RTLOGGRPFLAGS_DJ, LOG_GROUP, "%s", psz);
226
227 RTStrFree(psz);
228}
229
230/**
231 * Helper for printing SOAP error messages.
232 * @param soap
233 */
234void WebLogSoapError(struct soap *soap)
235{
236 if (soap_check_state(soap))
237 {
238 WebLog("Error: soap struct not initialized\n");
239 return;
240 }
241
242 const char *pcszFaultString = *soap_faultstring(soap);
243 const char **ppcszDetail = soap_faultcode(soap);
244 WebLog("#### SOAP FAULT: %s [%s]\n",
245 pcszFaultString ? pcszFaultString : "[no fault string available]",
246 (ppcszDetail && *ppcszDetail) ? *ppcszDetail : "no details available");
247}
248
249
250/**
251 * Start up the webservice server. This keeps running and waits
252 * for incoming SOAP connections; for each request that comes in,
253 * it calls method implementation code, most of it in the generated
254 * code in methodmaps.cpp.
255 *
256 * @param argc
257 * @param argv[]
258 * @return
259 */
260int main(int argc, char* argv[])
261{
262 int rc;
263
264 // intialize runtime
265 RTR3Init();
266
267 RTStrmPrintf(g_pStdErr, "Sun VirtualBox Webservice Version %s\n"
268 "(C) 2005-2009 Sun Microsystems, Inc.\n"
269 "All rights reserved.\n", VBOX_VERSION_STRING);
270
271 int c;
272 RTGETOPTUNION ValueUnion;
273 RTGETOPTSTATE GetState;
274 RTGetOptInit(&GetState, argc, argv, g_aOptions, RT_ELEMENTS(g_aOptions), 1, 0 /* fFlags */);
275 while ((c = RTGetOpt(&GetState, &ValueUnion)))
276 {
277 switch (c)
278 {
279 case 'H':
280 g_pcszBindToHost = ValueUnion.psz;
281 break;
282
283 case 'p':
284 g_uBindToPort = ValueUnion.u32;
285 break;
286
287 case 't':
288 g_iWatchdogTimeoutSecs = ValueUnion.u32;
289 break;
290
291 case 'i':
292 g_iWatchdogCheckInterval = ValueUnion.u32;
293 break;
294
295 case 'F':
296 {
297 int rc2 = RTStrmOpen(ValueUnion.psz, "a", &g_pstrLog);
298 if (rc2)
299 {
300 RTPrintf("Error: Cannot open log file \"%s\" for writing, error %d.\n", ValueUnion.psz, rc2);
301 exit(2);
302 }
303
304 WebLog("Sun VirtualBox Webservice Version %s\n"
305 "Opened log file \"%s\"\n", VBOX_VERSION_STRING, ValueUnion.psz);
306 }
307 break;
308
309 case 'h':
310 DisplayHelp();
311 exit(0);
312 break;
313
314 case 'v':
315 g_fVerbose = true;
316 break;
317
318#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
319 case 'b':
320 g_fDaemonize = true;
321 break;
322#endif
323 case VINF_GETOPT_NOT_OPTION:
324 RTStrmPrintf(g_pStdErr, "unhandled parameter: %s\n", ValueUnion.psz);
325 return 1;
326
327 default:
328 if (c > 0)
329 {
330 if (RT_C_IS_GRAPH(c))
331 RTStrmPrintf(g_pStdErr, "unhandled option: -%c", c);
332 else
333 RTStrmPrintf(g_pStdErr, "unhandled option: %i", c);
334 }
335 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
336 RTStrmPrintf(g_pStdErr, "unknown option: %s", ValueUnion.psz);
337 else if (ValueUnion.pDef)
338 RTStrmPrintf(g_pStdErr, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
339 else
340 RTStrmPrintf(g_pStdErr, "%Rrs", c);
341 exit(1);
342 break;
343 }
344 }
345
346#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
347 if (g_fDaemonize)
348 {
349 rc = RTProcDaemonize(false /* fNoChDir */, false /* fNoClose */,
350 NULL);
351 if (RT_FAILURE(rc))
352 {
353 RTStrmPrintf(g_pStdErr, "vboxwebsrv: failed to daemonize, rc=%Rrc. exiting.\n", rc);
354 exit(1);
355 }
356 }
357#endif
358
359 // intialize COM/XPCOM
360 rc = com::Initialize();
361 if (FAILED(rc))
362 {
363 RTPrintf("ERROR: failed to initialize COM!\n");
364 return rc;
365 }
366
367 ComPtr<ISession> session;
368
369 rc = g_pVirtualBox.createLocalObject(CLSID_VirtualBox);
370 if (FAILED(rc))
371 RTPrintf("ERROR: failed to create the VirtualBox object!\n");
372 else
373 {
374 rc = session.createInprocObject(CLSID_Session);
375 if (FAILED(rc))
376 RTPrintf("ERROR: failed to create a session object!\n");
377 }
378
379 if (FAILED(rc))
380 {
381 com::ErrorInfo info;
382 if (!info.isFullAvailable() && !info.isBasicAvailable())
383 {
384 com::GluePrintRCMessage(rc);
385 RTPrintf("Most likely, the VirtualBox COM server is not running or failed to start.\n");
386 }
387 else
388 com::GluePrintErrorInfo(info);
389 return rc;
390 }
391
392 if (g_iWatchdogTimeoutSecs > 0)
393 {
394 // start our watchdog thread
395 RTTHREAD tWatchdog;
396 if (RTThreadCreate(&tWatchdog,
397 fntWatchdog,
398 NULL,
399 32*1024,
400 RTTHREADTYPE_MAIN_WORKER,
401 0,
402 "Watchdog"))
403 {
404 RTStrmPrintf(g_pStdErr, "[!] Cannot start watchdog thread\n");
405 exit(1);
406 }
407 }
408
409 // set up gSOAP
410 struct soap soap;
411 soap_init(&soap);
412
413 soap.bind_flags |= SO_REUSEADDR;
414 // avoid EADDRINUSE on bind()
415
416 int m, s; // master and slave sockets
417 m = soap_bind(&soap,
418 g_pcszBindToHost, // host: current machine
419 g_uBindToPort, // port
420 g_uBacklog); // backlog = max queue size for requests
421 if (m < 0)
422 WebLogSoapError(&soap);
423 else
424 {
425 WebLog("Socket connection successful: host = %s, port = %u, master socket = %d\n",
426 (g_pcszBindToHost) ? g_pcszBindToHost : "default (localhost)",
427 g_uBindToPort,
428 m);
429
430 for (uint64_t i = 1;
431 ;
432 i++)
433 {
434 // call gSOAP to handle incoming SOAP connection
435 s = soap_accept(&soap);
436 if (s < 0)
437 {
438 WebLogSoapError(&soap);
439 break;
440 }
441
442 WebLog("%llu: accepted connection from IP=%lu.%lu.%lu.%lu socket=%d... ",
443 i,
444 (soap.ip>>24)&0xFF,
445 (soap.ip>>16)&0xFF,
446 (soap.ip>>8)&0xFF,
447 soap.ip&0xFF,
448 s);
449
450 // enclose the entire RPC call in the sessions lock
451 // so that the watchdog cannot destroy COM objects
452 // while the RPC is ongoing
453 RTLock lock(g_mutexSessions);
454 // now process the RPC request (this goes into the
455 // generated code in methodmaps.cpp with all the COM calls)
456 if (soap_serve(&soap) != SOAP_OK)
457 {
458 WebLogSoapError(&soap);
459 }
460 lock.release();
461
462 WebLog("Request served\n");
463
464 soap_destroy(&soap); // clean up class instances
465 soap_end(&soap); // clean up everything and close socket
466
467 // we have to process main event queue
468 int vrc = com::EventQueue::getMainEventQueue()->processEventQueue(0);
469 }
470 }
471 soap_done(&soap); // close master socket and detach environment
472
473 com::Shutdown();
474}
475
476/****************************************************************************
477 *
478 * Watchdog thread
479 *
480 ****************************************************************************/
481
482/**
483 * Watchdog thread, runs in the background while the webservice is alive.
484 *
485 * This gets started by main() and runs in the background to check all sessions
486 * for whether they have been no requests in a configurable timeout period. In
487 * that case, the session is automatically logged off.
488 */
489int fntWatchdog(RTTHREAD ThreadSelf, void *pvUser)
490{
491 WEBDEBUG(("Watchdog thread started\n"));
492
493 while (1)
494 {
495 WEBDEBUG(("Watchdog: sleeping %d seconds\n", g_iWatchdogCheckInterval));
496 RTThreadSleep(g_iWatchdogCheckInterval * 1000);
497
498 time_t tNow;
499 time(&tNow);
500
501 RTLock lock(g_mutexSessions);
502 WEBDEBUG(("Watchdog: checking %d sessions\n", g_mapSessions.size()));
503
504 SessionsMap::iterator
505 it = g_mapSessions.begin(),
506 itEnd = g_mapSessions.end();
507 while (it != itEnd)
508 {
509 WebServiceSession *pSession = it->second;
510 WEBDEBUG(("Watchdog: tNow: %d, session timestamp: %d\n", tNow, pSession->getLastObjectLookup()));
511 if ( tNow
512 > pSession->getLastObjectLookup() + g_iWatchdogTimeoutSecs
513 )
514 {
515 WEBDEBUG(("Watchdog: Session %llX timed out, deleting\n", pSession->getID()));
516 delete pSession;
517 it = g_mapSessions.begin();
518 }
519 else
520 ++it;
521 }
522 lock.release();
523 }
524
525 WEBDEBUG(("Watchdog thread ending\n"));
526 return 0;
527}
528
529/****************************************************************************
530 *
531 * SOAP exceptions
532 *
533 ****************************************************************************/
534
535/**
536 * Helper function to raise a SOAP fault. Called by the other helper
537 * functions, which raise specific SOAP faults.
538 *
539 * @param soap
540 * @param str
541 * @param extype
542 * @param ex
543 */
544void RaiseSoapFault(struct soap *soap,
545 const char *pcsz,
546 int extype,
547 void *ex)
548{
549 // raise the fault
550 soap_sender_fault(soap, pcsz, NULL);
551
552 struct SOAP_ENV__Detail *pDetail = (struct SOAP_ENV__Detail*)soap_malloc(soap, sizeof(struct SOAP_ENV__Detail));
553
554 // without the following, gSOAP crashes miserably when sending out the
555 // data because it will try to serialize all fields (stupid documentation)
556 memset(pDetail, 0, sizeof(struct SOAP_ENV__Detail));
557
558 // fill extended info depending on SOAP version
559 if (soap->version == 2) // SOAP 1.2 is used
560 {
561 soap->fault->SOAP_ENV__Detail = pDetail;
562 soap->fault->SOAP_ENV__Detail->__type = extype;
563 soap->fault->SOAP_ENV__Detail->fault = ex;
564 soap->fault->SOAP_ENV__Detail->__any = NULL; // no other XML data
565 }
566 else
567 {
568 soap->fault->detail = pDetail;
569 soap->fault->detail->__type = extype;
570 soap->fault->detail->fault = ex;
571 soap->fault->detail->__any = NULL; // no other XML data
572 }
573}
574
575/**
576 * Raises a SOAP fault that signals that an invalid object was passed.
577 *
578 * @param soap
579 * @param obj
580 */
581void RaiseSoapInvalidObjectFault(struct soap *soap,
582 WSDLT_ID obj)
583{
584 _vbox__InvalidObjectFault *ex = soap_new__vbox__InvalidObjectFault(soap, 1);
585 ex->badObjectID = obj;
586
587 std::string str("VirtualBox error: ");
588 str += "Invalid managed object reference \"" + obj + "\"";
589
590 RaiseSoapFault(soap,
591 str.c_str(),
592 SOAP_TYPE__vbox__InvalidObjectFault,
593 ex);
594}
595
596/**
597 * Return a safe C++ string from the given COM string,
598 * without crashing if the COM string is empty.
599 * @param bstr
600 * @return
601 */
602std::string ConvertComString(const com::Bstr &bstr)
603{
604 com::Utf8Str ustr(bstr);
605 const char *pcsz;
606 if ((pcsz = ustr.raw()))
607 return pcsz;
608 return "";
609}
610
611/**
612 * Return a safe C++ string from the given COM UUID,
613 * without crashing if the UUID is empty.
614 * @param bstr
615 * @return
616 */
617std::string ConvertComString(const com::Guid &uuid)
618{
619 com::Bstr bstr(uuid);
620 com::Utf8Str ustr(bstr);
621 const char *pcsz;
622 if ((pcsz = ustr.raw()))
623 return pcsz;
624 return "";
625}
626
627/**
628 * Raises a SOAP runtime fault.
629 *
630 * @param pObj
631 */
632void RaiseSoapRuntimeFault(struct soap *soap,
633 HRESULT apirc,
634 IUnknown *pObj)
635{
636 com::ErrorInfo info(pObj);
637
638 WEBDEBUG((" error, raising SOAP exception\n"));
639
640 RTStrmPrintf(g_pStdErr, "API return code: 0x%08X (%Rhrc)\n", apirc, apirc);
641 RTStrmPrintf(g_pStdErr, "COM error info result code: 0x%lX\n", info.getResultCode());
642 RTStrmPrintf(g_pStdErr, "COM error info text: %ls\n", info.getText().raw());
643
644 // allocated our own soap fault struct
645 _vbox__RuntimeFault *ex = soap_new__vbox__RuntimeFault(soap, 1);
646 ex->resultCode = info.getResultCode();
647 ex->text = ConvertComString(info.getText());
648 ex->component = ConvertComString(info.getComponent());
649 ex->interfaceID = ConvertComString(info.getInterfaceID());
650
651 // compose descriptive message
652 com::Utf8StrFmt str("VirtualBox error: %s (0x%RU32)", ex->text.c_str(), ex->resultCode);
653
654 RaiseSoapFault(soap,
655 str.c_str(),
656 SOAP_TYPE__vbox__RuntimeFault,
657 ex);
658}
659
660/****************************************************************************
661 *
662 * splitting and merging of object IDs
663 *
664 ****************************************************************************/
665
666uint64_t str2ulonglong(const char *pcsz)
667{
668 uint64_t u = 0;
669 RTStrToUInt64Full(pcsz, 16, &u);
670 return u;
671}
672
673/**
674 * Splits a managed object reference (in string form, as
675 * passed in from a SOAP method call) into two integers for
676 * session and object IDs, respectively.
677 *
678 * @param id
679 * @param sessid
680 * @param objid
681 * @return
682 */
683bool SplitManagedObjectRef(const WSDLT_ID &id,
684 uint64_t *pSessid,
685 uint64_t *pObjid)
686{
687 // 64-bit numbers in hex have 16 digits; hence
688 // the object-ref string must have 16 + "-" + 16 characters
689 std::string str;
690 if ( (id.length() == 33)
691 && (id[16] == '-')
692 )
693 {
694 char psz[34];
695 memcpy(psz, id.c_str(), 34);
696 psz[16] = '\0';
697 if (pSessid)
698 *pSessid = str2ulonglong(psz);
699 if (pObjid)
700 *pObjid = str2ulonglong(psz + 17);
701 return true;
702 }
703
704 return false;
705}
706
707/**
708 * Creates a managed object reference (in string form) from
709 * two integers representing a session and object ID, respectively.
710 *
711 * @param sz Buffer with at least 34 bytes space to receive MOR string.
712 * @param sessid
713 * @param objid
714 * @return
715 */
716void MakeManagedObjectRef(char *sz,
717 uint64_t &sessid,
718 uint64_t &objid)
719{
720 RTStrFormatNumber(sz, sessid, 16, 16, 0, RTSTR_F_64BIT | RTSTR_F_ZEROPAD);
721 sz[16] = '-';
722 RTStrFormatNumber(sz + 17, objid, 16, 16, 0, RTSTR_F_64BIT | RTSTR_F_ZEROPAD);
723}
724
725/****************************************************************************
726 *
727 * class WebServiceSession
728 *
729 ****************************************************************************/
730
731class WebServiceSessionPrivate
732{
733 public:
734 ManagedObjectsMapById _mapManagedObjectsById;
735 ManagedObjectsMapByPtr _mapManagedObjectsByPtr;
736};
737
738/**
739 * Constructor for the session object.
740 *
741 * Preconditions: Caller must have locked g_mutexSessions.
742 *
743 * @param username
744 * @param password
745 */
746WebServiceSession::WebServiceSession()
747 : _fDestructing(false),
748 _pISession(NULL),
749 _tLastObjectLookup(0)
750{
751 _pp = new WebServiceSessionPrivate;
752 _uSessionID = RTRandU64();
753
754 // register this session globally
755 g_mapSessions[_uSessionID] = this;
756}
757
758/**
759 * Destructor. Cleans up and destroys all contained managed object references on the way.
760 *
761 * Preconditions: Caller must have locked g_mutexSessions.
762 */
763WebServiceSession::~WebServiceSession()
764{
765 // delete us from global map first so we can't be found
766 // any more while we're cleaning up
767 g_mapSessions.erase(_uSessionID);
768
769 // notify ManagedObjectRef destructor so it won't
770 // remove itself from the maps; this avoids rebalancing
771 // the map's tree on every delete as well
772 _fDestructing = true;
773
774 // if (_pISession)
775 // {
776 // delete _pISession;
777 // _pISession = NULL;
778 // }
779
780 ManagedObjectsMapById::iterator
781 it,
782 end = _pp->_mapManagedObjectsById.end();
783 for (it = _pp->_mapManagedObjectsById.begin();
784 it != end;
785 ++it)
786 {
787 ManagedObjectRef *pRef = it->second;
788 delete pRef; // this frees the contained ComPtr as well
789 }
790
791 delete _pp;
792}
793
794/**
795 * Authenticate the username and password against an authentification authority.
796 *
797 * @return 0 if the user was successfully authenticated, or an error code
798 * otherwise.
799 */
800
801int WebServiceSession::authenticate(const char *pcszUsername,
802 const char *pcszPassword)
803{
804 int rc = VERR_WEB_NOT_AUTHENTICATED;
805
806 RTLock lock(g_mutexAuthLib);
807
808 static bool fAuthLibLoaded = false;
809 static PVRDPAUTHENTRY pfnAuthEntry = NULL;
810 static PVRDPAUTHENTRY2 pfnAuthEntry2 = NULL;
811
812 if (!fAuthLibLoaded)
813 {
814 // retrieve authentication library from system properties
815 ComPtr<ISystemProperties> systemProperties;
816 g_pVirtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
817
818 com::Bstr authLibrary;
819 systemProperties->COMGETTER(WebServiceAuthLibrary)(authLibrary.asOutParam());
820 com::Utf8Str filename = authLibrary;
821
822 WEBDEBUG(("external authentication library is '%ls'\n", authLibrary.raw()));
823
824 if (filename == "null")
825 // authentication disabled, let everyone in:
826 fAuthLibLoaded = true;
827 else
828 {
829 RTLDRMOD hlibAuth = 0;
830 do
831 {
832 rc = RTLdrLoad(filename.raw(), &hlibAuth);
833 if (RT_FAILURE(rc))
834 {
835 WEBDEBUG(("%s() Failed to load external authentication library. Error code: %Rrc\n", __FUNCTION__, rc));
836 break;
837 }
838
839 if (RT_FAILURE(rc = RTLdrGetSymbol(hlibAuth, "VRDPAuth2", (void**)&pfnAuthEntry2)))
840 WEBDEBUG(("%s(): Could not resolve import '%s'. Error code: %Rrc\n", __FUNCTION__, "VRDPAuth2", rc));
841
842 if (RT_FAILURE(rc = RTLdrGetSymbol(hlibAuth, "VRDPAuth", (void**)&pfnAuthEntry)))
843 WEBDEBUG(("%s(): Could not resolve import '%s'. Error code: %Rrc\n", __FUNCTION__, "VRDPAuth", rc));
844
845 if (pfnAuthEntry || pfnAuthEntry2)
846 fAuthLibLoaded = true;
847
848 } while (0);
849 }
850 }
851
852 rc = VERR_WEB_NOT_AUTHENTICATED;
853 VRDPAuthResult result;
854 if (pfnAuthEntry2)
855 {
856 result = pfnAuthEntry2(NULL, VRDPAuthGuestNotAsked, pcszUsername, pcszPassword, NULL, true, 0);
857 WEBDEBUG(("%s(): result of VRDPAuth2(): %d\n", __FUNCTION__, result));
858 if (result == VRDPAuthAccessGranted)
859 rc = 0;
860 }
861 else if (pfnAuthEntry)
862 {
863 result = pfnAuthEntry(NULL, VRDPAuthGuestNotAsked, pcszUsername, pcszPassword, NULL);
864 WEBDEBUG(("%s(): result of VRDPAuth(%s, [%d]): %d\n", __FUNCTION__, pcszUsername, strlen(pcszPassword), result));
865 if (result == VRDPAuthAccessGranted)
866 rc = 0;
867 }
868 else if (fAuthLibLoaded)
869 // fAuthLibLoaded = true but both pointers are NULL:
870 // then the authlib was "null" and auth was disabled
871 rc = 0;
872 else
873 {
874 WEBDEBUG(("Could not resolve VRDPAuth2 or VRDPAuth entry point"));
875 }
876
877 if (!rc)
878 {
879 do
880 {
881 // now create the ISession object that this webservice session can use
882 // (and of which IWebsessionManager::getSessionObject returns a managed object reference)
883 ComPtr<ISession> session;
884 if (FAILED(rc = session.createInprocObject(CLSID_Session)))
885 {
886 WEBDEBUG(("ERROR: cannot create session object!"));
887 break;
888 }
889
890 _pISession = new ManagedObjectRef(*this, g_pcszISession, session);
891
892 if (g_fVerbose)
893 {
894 ISession *p = session;
895 std::string strMOR = _pISession->toWSDL();
896 WEBDEBUG((" * %s: created session object with comptr 0x%lX, MOR = %s\n", __FUNCTION__, p, strMOR.c_str()));
897 }
898 } while (0);
899 }
900
901 return rc;
902}
903
904/**
905 * Look up, in this session, whether a ManagedObjectRef has already been
906 * created for the given COM pointer.
907 *
908 * Note how we require that a ComPtr<IUnknown> is passed, which causes a
909 * queryInterface call when the caller passes in a different type, since
910 * a ComPtr<IUnknown> will point to something different than a
911 * ComPtr<IVirtualBox>, for example. As we store the ComPtr<IUnknown> in
912 * our private hash table, we must search for one too.
913 *
914 * Preconditions: Caller must have locked g_mutexSessions.
915 *
916 * @param pcu pointer to a COM object.
917 * @return The existing ManagedObjectRef that represents the COM object, or NULL if there's none yet.
918 */
919ManagedObjectRef* WebServiceSession::findRefFromPtr(const ComPtr<IUnknown> &pcu)
920{
921 IUnknown *p = pcu;
922 uintptr_t ulp = (uintptr_t)p;
923 ManagedObjectRef *pRef;
924 // WEBDEBUG((" %s: looking up 0x%lX\n", __FUNCTION__, ulp));
925 ManagedObjectsMapByPtr::iterator it = _pp->_mapManagedObjectsByPtr.find(ulp);
926 if (it != _pp->_mapManagedObjectsByPtr.end())
927 {
928 pRef = it->second;
929 WSDLT_ID id = pRef->toWSDL();
930 WEBDEBUG((" %s: found existing ref %s for COM obj 0x%lX\n", __FUNCTION__, id.c_str(), ulp));
931 }
932 else
933 pRef = NULL;
934 return pRef;
935}
936
937/**
938 * Static method which attempts to find the session for which the given managed
939 * object reference was created, by splitting the reference into the session and
940 * object IDs and then looking up the session object for that session ID.
941 *
942 * Preconditions: Caller must have locked g_mutexSessions.
943 *
944 * @param id Managed object reference (with combined session and object IDs).
945 * @return
946 */
947WebServiceSession* WebServiceSession::findSessionFromRef(const WSDLT_ID &id)
948{
949 WebServiceSession *pSession = NULL;
950 uint64_t sessid;
951 if (SplitManagedObjectRef(id,
952 &sessid,
953 NULL))
954 {
955 SessionsMapIterator it = g_mapSessions.find(sessid);
956 if (it != g_mapSessions.end())
957 pSession = it->second;
958 }
959 return pSession;
960}
961
962/**
963 *
964 */
965WSDLT_ID WebServiceSession::getSessionObject() const
966{
967 return _pISession->toWSDL();
968}
969
970/**
971 * Touches the webservice session to prevent it from timing out.
972 *
973 * Each webservice session has an internal timestamp that records
974 * the last request made to it from the client that started it.
975 * If no request was made within a configurable timeframe, then
976 * the client is logged off automatically,
977 * by calling IWebsessionManager::logoff()
978 */
979void WebServiceSession::touch()
980{
981 time(&_tLastObjectLookup);
982}
983
984/**
985 *
986 */
987void WebServiceSession::DumpRefs()
988{
989 WEBDEBUG((" dumping object refs:\n"));
990 ManagedObjectsIteratorById
991 iter = _pp->_mapManagedObjectsById.begin(),
992 end = _pp->_mapManagedObjectsById.end();
993 for (;
994 iter != end;
995 ++iter)
996 {
997 ManagedObjectRef *pRef = iter->second;
998 uint64_t id = pRef->getID();
999 void *p = pRef->getComPtr();
1000 WEBDEBUG((" objid %llX: comptr 0x%lX\n", id, p));
1001 }
1002}
1003
1004/****************************************************************************
1005 *
1006 * class ManagedObjectRef
1007 *
1008 ****************************************************************************/
1009
1010/**
1011 * Constructor, which assigns a unique ID to this managed object
1012 * reference and stores it two global hashs:
1013 *
1014 * a) G_mapManagedObjectsById, which maps ManagedObjectID's to
1015 * instances of this class; this hash is then used by the
1016 * findObjectFromRef() template function in vboxweb.h
1017 * to quickly retrieve the COM object from its managed
1018 * object ID (mostly in the context of the method mappers
1019 * in methodmaps.cpp, when a web service client passes in
1020 * a managed object ID);
1021 *
1022 * b) G_mapManagedObjectsByComPtr, which maps COM pointers to
1023 * instances of this class; this hash is used by
1024 * createRefFromObject() to quickly figure out whether an
1025 * instance already exists for a given COM pointer.
1026 *
1027 * This does _not_ check whether another instance already
1028 * exists in the hash. This gets called only from the
1029 * createRefFromObject() template function in vboxweb.h, which
1030 * does perform that check.
1031 *
1032 * Preconditions: Caller must have locked g_mutexSessions.
1033 *
1034 * @param pObj
1035 */
1036ManagedObjectRef::ManagedObjectRef(WebServiceSession &session,
1037 const char *pcszInterface,
1038 const ComPtr<IUnknown> &pc)
1039 : _session(session),
1040 _pObj(pc),
1041 _pcszInterface(pcszInterface)
1042{
1043 ComPtr<IUnknown> pcUnknown(pc);
1044 _ulp = (uintptr_t)(IUnknown*)pcUnknown;
1045
1046 _id = ++g_iMaxManagedObjectID;
1047 // and count globally
1048 ULONG64 cTotal = ++g_cManagedObjects; // raise global count and make a copy for the debug message below
1049
1050 char sz[34];
1051 MakeManagedObjectRef(sz, session._uSessionID, _id);
1052 _strID = sz;
1053
1054 session._pp->_mapManagedObjectsById[_id] = this;
1055 session._pp->_mapManagedObjectsByPtr[_ulp] = this;
1056
1057 session.touch();
1058
1059 WEBDEBUG((" * %s: MOR created for ulp 0x%lX (%s), new ID is %llX; now %lld objects total\n", __FUNCTION__, _ulp, pcszInterface, _id, cTotal));
1060}
1061
1062/**
1063 * Destructor; removes the instance from the global hash of
1064 * managed objects.
1065 *
1066 * Preconditions: Caller must have locked g_mutexSessions.
1067 */
1068ManagedObjectRef::~ManagedObjectRef()
1069{
1070 ULONG64 cTotal = --g_cManagedObjects;
1071
1072 WEBDEBUG((" * %s: deleting MOR for ID %llX (%s); now %lld objects total\n", __FUNCTION__, _id, _pcszInterface, cTotal));
1073
1074 // if we're being destroyed from the session's destructor,
1075 // then that destructor is iterating over the maps, so
1076 // don't remove us there! (data integrity + speed)
1077 if (!_session._fDestructing)
1078 {
1079 WEBDEBUG((" * %s: removing from session maps\n", __FUNCTION__));
1080 _session._pp->_mapManagedObjectsById.erase(_id);
1081 if (_session._pp->_mapManagedObjectsByPtr.erase(_ulp) != 1)
1082 WEBDEBUG((" WARNING: could not find %llX in _mapManagedObjectsByPtr\n", _ulp));
1083 }
1084}
1085
1086/**
1087 * Converts the ID of this managed object reference to string
1088 * form, for returning with SOAP data or similar.
1089 *
1090 * @return The ID in string form.
1091 */
1092WSDLT_ID ManagedObjectRef::toWSDL() const
1093{
1094 return _strID;
1095}
1096
1097/**
1098 * Static helper method for findObjectFromRef() template that actually
1099 * looks up the object from a given integer ID.
1100 *
1101 * This has been extracted into this non-template function to reduce
1102 * code bloat as we have the actual STL map lookup only in this function.
1103 *
1104 * This also "touches" the timestamp in the session whose ID is encoded
1105 * in the given integer ID, in order to prevent the session from timing
1106 * out.
1107 *
1108 * Preconditions: Caller must have locked g_mutexSessions.
1109 *
1110 * @param strId
1111 * @param iter
1112 * @return
1113 */
1114int ManagedObjectRef::findRefFromId(const WSDLT_ID &id,
1115 ManagedObjectRef **pRef,
1116 bool fNullAllowed)
1117{
1118 int rc = 0;
1119
1120 do
1121 {
1122 // allow NULL (== empty string) input reference, which should return a NULL pointer
1123 if (!id.length() && fNullAllowed)
1124 {
1125 *pRef = NULL;
1126 return 0;
1127 }
1128
1129 uint64_t sessid;
1130 uint64_t objid;
1131 WEBDEBUG((" %s(): looking up objref %s\n", __FUNCTION__, id.c_str()));
1132 if (!SplitManagedObjectRef(id,
1133 &sessid,
1134 &objid))
1135 {
1136 rc = VERR_WEB_INVALID_MANAGED_OBJECT_REFERENCE;
1137 break;
1138 }
1139
1140 WEBDEBUG((" %s(): sessid %llX, objid %llX\n", __FUNCTION__, sessid, objid));
1141 SessionsMapIterator it = g_mapSessions.find(sessid);
1142 if (it == g_mapSessions.end())
1143 {
1144 WEBDEBUG((" %s: cannot find session for objref %s\n", __FUNCTION__, id.c_str()));
1145 rc = VERR_WEB_INVALID_SESSION_ID;
1146 break;
1147 }
1148
1149 WebServiceSession *pSess = it->second;
1150 // "touch" session to prevent it from timing out
1151 pSess->touch();
1152
1153 ManagedObjectsIteratorById iter = pSess->_pp->_mapManagedObjectsById.find(objid);
1154 if (iter == pSess->_pp->_mapManagedObjectsById.end())
1155 {
1156 WEBDEBUG((" %s: cannot find comobj for objref %s\n", __FUNCTION__, id.c_str()));
1157 rc = VERR_WEB_INVALID_OBJECT_ID;
1158 break;
1159 }
1160
1161 *pRef = iter->second;
1162
1163 } while (0);
1164
1165 return rc;
1166}
1167
1168/****************************************************************************
1169 *
1170 * interface IManagedObjectRef
1171 *
1172 ****************************************************************************/
1173
1174/**
1175 * This is the hard-coded implementation for the IManagedObjectRef::getInterfaceName()
1176 * that our WSDL promises to our web service clients. This method returns a
1177 * string describing the interface that this managed object reference
1178 * supports, e.g. "IMachine".
1179 *
1180 * @param soap
1181 * @param req
1182 * @param resp
1183 * @return
1184 */
1185int __vbox__IManagedObjectRef_USCOREgetInterfaceName(
1186 struct soap *soap,
1187 _vbox__IManagedObjectRef_USCOREgetInterfaceName *req,
1188 _vbox__IManagedObjectRef_USCOREgetInterfaceNameResponse *resp)
1189{
1190 HRESULT rc = SOAP_OK;
1191 WEBDEBUG(("\n-- entering %s\n", __FUNCTION__));
1192
1193 do {
1194 ManagedObjectRef *pRef;
1195 if (!ManagedObjectRef::findRefFromId(req->_USCOREthis, &pRef, false))
1196 resp->returnval = pRef->getInterfaceName();
1197
1198 } while (0);
1199
1200 WEBDEBUG(("-- leaving %s, rc: 0x%lX\n", __FUNCTION__, rc));
1201 if (FAILED(rc))
1202 return SOAP_FAULT;
1203 return SOAP_OK;
1204}
1205
1206/**
1207 * This is the hard-coded implementation for the IManagedObjectRef::release()
1208 * that our WSDL promises to our web service clients. This method releases
1209 * a managed object reference and removes it from our stacks.
1210 *
1211 * @param soap
1212 * @param req
1213 * @param resp
1214 * @return
1215 */
1216int __vbox__IManagedObjectRef_USCORErelease(
1217 struct soap *soap,
1218 _vbox__IManagedObjectRef_USCORErelease *req,
1219 _vbox__IManagedObjectRef_USCOREreleaseResponse *resp)
1220{
1221 HRESULT rc = SOAP_OK;
1222 WEBDEBUG(("\n-- entering %s\n", __FUNCTION__));
1223
1224 do {
1225 ManagedObjectRef *pRef;
1226 if ((rc = ManagedObjectRef::findRefFromId(req->_USCOREthis, &pRef, false)))
1227 {
1228 RaiseSoapInvalidObjectFault(soap, req->_USCOREthis);
1229 break;
1230 }
1231
1232 WEBDEBUG((" found reference; deleting!\n"));
1233 delete pRef;
1234 // this removes the object from all stacks; since
1235 // there's a ComPtr<> hidden inside the reference,
1236 // this should also invoke Release() on the COM
1237 // object
1238 } while (0);
1239
1240 WEBDEBUG(("-- leaving %s, rc: 0x%lX\n", __FUNCTION__, rc));
1241 if (FAILED(rc))
1242 return SOAP_FAULT;
1243 return SOAP_OK;
1244}
1245
1246/****************************************************************************
1247 *
1248 * interface IWebsessionManager
1249 *
1250 ****************************************************************************/
1251
1252/**
1253 * Hard-coded implementation for IWebsessionManager::logon. As opposed to the underlying
1254 * COM API, this is the first method that a webservice client must call before the
1255 * webservice will do anything useful.
1256 *
1257 * This returns a managed object reference to the global IVirtualBox object; into this
1258 * reference a session ID is encoded which remains constant with all managed object
1259 * references returned by other methods.
1260 *
1261 * This also creates an instance of ISession, which is stored internally with the
1262 * webservice session and can be retrieved with IWebsessionManager::getSessionObject
1263 * (__vbox__IWebsessionManager_USCOREgetSessionObject). In order for the
1264 * VirtualBox web service to do anything useful, one usually needs both a
1265 * VirtualBox and an ISession object, for which these two methods are designed.
1266 *
1267 * When the webservice client is done, it should call IWebsessionManager::logoff. This
1268 * will clean up internally (destroy all remaining managed object references and
1269 * related COM objects used internally).
1270 *
1271 * After logon, an internal timeout ensures that if the webservice client does not
1272 * call any methods, after a configurable number of seconds, the webservice will log
1273 * off the client automatically. This is to ensure that the webservice does not
1274 * drown in managed object references and eventually deny service. Still, it is
1275 * a much better solution, both for performance and cleanliness, for the webservice
1276 * client to clean up itself.
1277 *
1278 * Preconditions: Caller must have locked g_mutexSessions.
1279 * Since this gets called from main() like other SOAP method
1280 * implementations, this is ensured.
1281 *
1282 * @param
1283 * @param vbox__IWebsessionManager_USCORElogon
1284 * @param vbox__IWebsessionManager_USCORElogonResponse
1285 * @return
1286 */
1287int __vbox__IWebsessionManager_USCORElogon(
1288 struct soap*,
1289 _vbox__IWebsessionManager_USCORElogon *req,
1290 _vbox__IWebsessionManager_USCORElogonResponse *resp)
1291{
1292 HRESULT rc = SOAP_OK;
1293 WEBDEBUG(("\n-- entering %s\n", __FUNCTION__));
1294
1295 do {
1296 // create new session; the constructor stores the new session
1297 // in the global map automatically
1298 WebServiceSession *pSession = new WebServiceSession();
1299
1300 // authenticate the user
1301 if (!(pSession->authenticate(req->username.c_str(),
1302 req->password.c_str())))
1303 {
1304 // in the new session, create a managed object reference (moref) for the
1305 // global VirtualBox object; this encodes the session ID in the moref so
1306 // that it will be implicitly be included in all future requests of this
1307 // webservice client
1308 ManagedObjectRef *pRef = new ManagedObjectRef(*pSession, g_pcszIVirtualBox, g_pVirtualBox);
1309 resp->returnval = pRef->toWSDL();
1310 WEBDEBUG(("VirtualBox object ref is %s\n", resp->returnval.c_str()));
1311 }
1312 } while (0);
1313
1314 WEBDEBUG(("-- leaving %s, rc: 0x%lX\n", __FUNCTION__, rc));
1315 if (FAILED(rc))
1316 return SOAP_FAULT;
1317 return SOAP_OK;
1318}
1319
1320/**
1321 * Returns the ISession object that was created for the webservice client
1322 * on logon.
1323 *
1324 * Preconditions: Caller must have locked g_mutexSessions.
1325 * Since this gets called from main() like other SOAP method
1326 * implementations, this is ensured.
1327 */
1328int __vbox__IWebsessionManager_USCOREgetSessionObject(
1329 struct soap*,
1330 _vbox__IWebsessionManager_USCOREgetSessionObject *req,
1331 _vbox__IWebsessionManager_USCOREgetSessionObjectResponse *resp)
1332{
1333 HRESULT rc = SOAP_OK;
1334 WEBDEBUG(("\n-- entering %s\n", __FUNCTION__));
1335
1336 do {
1337 WebServiceSession* pSession;
1338 if ((pSession = WebServiceSession::findSessionFromRef(req->refIVirtualBox)))
1339 {
1340 resp->returnval = pSession->getSessionObject();
1341 }
1342 } while (0);
1343
1344 WEBDEBUG(("-- leaving %s, rc: 0x%lX\n", __FUNCTION__, rc));
1345 if (FAILED(rc))
1346 return SOAP_FAULT;
1347 return SOAP_OK;
1348}
1349
1350/**
1351 * hard-coded implementation for IWebsessionManager::logoff.
1352 *
1353 * Preconditions: Caller must have locked g_mutexSessions.
1354 * Since this gets called from main() like other SOAP method
1355 * implementations, this is ensured.
1356 *
1357 * @param
1358 * @param vbox__IWebsessionManager_USCORElogon
1359 * @param vbox__IWebsessionManager_USCORElogonResponse
1360 * @return
1361 */
1362int __vbox__IWebsessionManager_USCORElogoff(
1363 struct soap*,
1364 _vbox__IWebsessionManager_USCORElogoff *req,
1365 _vbox__IWebsessionManager_USCORElogoffResponse *resp)
1366{
1367 HRESULT rc = SOAP_OK;
1368 WEBDEBUG(("\n-- entering %s\n", __FUNCTION__));
1369
1370 do {
1371 WebServiceSession* pSession;
1372 if ((pSession = WebServiceSession::findSessionFromRef(req->refIVirtualBox)))
1373 {
1374 delete pSession;
1375 // destructor cleans up
1376
1377 WEBDEBUG(("session destroyed, %d sessions left open\n", g_mapSessions.size()));
1378 }
1379 } while (0);
1380
1381 WEBDEBUG(("-- leaving %s, rc: 0x%lX\n", __FUNCTION__, rc));
1382 if (FAILED(rc))
1383 return SOAP_FAULT;
1384 return SOAP_OK;
1385}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use