VirtualBox

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

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

Web service: make no. of threads a cmd line param, encapsulate queue code

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette