VirtualBox

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

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

Webservice: fix unlikely potential race in IWebsessionManager::GetSessionObject()

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