Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include <stdio.h>
21 :
22 : #include "sal/main.h"
23 : #include <osl/diagnose.h>
24 : #include <osl/mutex.hxx>
25 : #include <osl/conditn.hxx>
26 :
27 : #include <rtl/process.h>
28 : #include <rtl/string.h>
29 : #include <rtl/strbuf.hxx>
30 : #include <rtl/ustrbuf.hxx>
31 :
32 : #include <cppuhelper/bootstrap.hxx>
33 : #include <cppuhelper/implbase1.hxx>
34 :
35 : #include <com/sun/star/lang/XMain.hpp>
36 : #include <com/sun/star/lang/XInitialization.hpp>
37 : #include <com/sun/star/lang/XComponent.hpp>
38 : #include <com/sun/star/lang/XSingleComponentFactory.hpp>
39 : #include <com/sun/star/lang/XSingleServiceFactory.hpp>
40 : #include <com/sun/star/lang/XEventListener.hpp>
41 : #include <com/sun/star/loader/XImplementationLoader.hpp>
42 : #include <com/sun/star/registry/XRegistryKey.hpp>
43 : #include <com/sun/star/connection/Acceptor.hpp>
44 : #include <com/sun/star/connection/XConnection.hpp>
45 : #include <com/sun/star/bridge/XBridgeFactory.hpp>
46 : #include <com/sun/star/bridge/XBridge.hpp>
47 :
48 : using namespace std;
49 : using namespace osl;
50 : using namespace cppu;
51 : using namespace com::sun::star::uno;
52 : using namespace com::sun::star::lang;
53 : using namespace com::sun::star::loader;
54 : using namespace com::sun::star::registry;
55 : using namespace com::sun::star::connection;
56 : using namespace com::sun::star::bridge;
57 : using namespace com::sun::star::container;
58 :
59 : namespace unoexe
60 : {
61 :
62 : static sal_Bool s_quiet = false;
63 :
64 0 : static inline void out( const sal_Char * pText )
65 : {
66 0 : if (! s_quiet)
67 0 : fprintf( stderr, "%s", pText );
68 0 : }
69 :
70 0 : static inline void out( const OUString & rText )
71 : {
72 0 : if (! s_quiet)
73 : {
74 0 : OString aText( OUStringToOString( rText, RTL_TEXTENCODING_ASCII_US ) );
75 0 : fprintf( stderr, "%s", aText.getStr() );
76 : }
77 0 : }
78 :
79 : static const char arUsingText[] =
80 : "\nusing:\n\n"
81 : "uno [-c ComponentImplementationName -l LocationUrl | -s ServiceName]\n"
82 : " [-u uno:(socket[,host=HostName][,port=nnn]|pipe[,name=PipeName]);<protocol>;Name\n"
83 : " [--singleaccept] [--singleinstance]]\n"
84 : " [--quiet]\n"
85 : " [-- Argument1 Argument2 ...]\n";
86 :
87 0 : static sal_Bool readOption( OUString * pValue, const sal_Char * pOpt,
88 : sal_uInt32 * pnIndex, const OUString & aArg)
89 : throw (RuntimeException)
90 : {
91 0 : const OUString dash("-");
92 0 : if(!aArg.startsWith(dash))
93 0 : return sal_False;
94 :
95 0 : OUString aOpt = OUString::createFromAscii( pOpt );
96 :
97 0 : if (aArg.getLength() < aOpt.getLength())
98 0 : return sal_False;
99 :
100 0 : if (aOpt.equalsIgnoreAsciiCase( aArg.copy(1) ))
101 : {
102 : // take next argument
103 0 : ++(*pnIndex);
104 :
105 0 : rtl_getAppCommandArg(*pnIndex, &pValue->pData);
106 0 : if (*pnIndex >= rtl_getAppCommandArgCount() || pValue->copy(1).equals(dash))
107 : {
108 : throw RuntimeException(
109 0 : "incomplete option \"-" + aOpt + "\" given!",
110 0 : Reference< XInterface >() );
111 : }
112 : else
113 : {
114 : #if OSL_DEBUG_LEVEL > 1
115 : out( "\n> identified option -" );
116 : out( pOpt );
117 : out( " = " );
118 : OString tmp = OUStringToOString(aArg, RTL_TEXTENCODING_ASCII_US);
119 : out( tmp.getStr() );
120 : #endif
121 0 : ++(*pnIndex);
122 0 : return sal_True;
123 : }
124 : }
125 0 : else if (aArg.indexOf(aOpt) == 1)
126 : {
127 0 : *pValue = aArg.copy(1 + aOpt.getLength());
128 : #if OSL_DEBUG_LEVEL > 1
129 : out( "\n> identified option -" );
130 : out( pOpt );
131 : out( " = " );
132 : OString tmp = OUStringToOString(aArg.copy(aOpt.getLength()), RTL_TEXTENCODING_ASCII_US);
133 : out( tmp.getStr() );
134 : #endif
135 0 : ++(*pnIndex);
136 :
137 0 : return sal_True;
138 : }
139 0 : return sal_False;
140 : }
141 :
142 0 : static sal_Bool readOption( sal_Bool * pbOpt, const sal_Char * pOpt,
143 : sal_uInt32 * pnIndex, const OUString & aArg)
144 : {
145 0 : const OUString dashdash("--");
146 0 : OUString aOpt = OUString::createFromAscii(pOpt);
147 :
148 0 : if(aArg.startsWith(dashdash) && aOpt.equals(aArg.copy(2)))
149 : {
150 0 : ++(*pnIndex);
151 0 : *pbOpt = sal_True;
152 : #if OSL_DEBUG_LEVEL > 1
153 : out( "\n> identified option --" );
154 : out( pOpt );
155 : #endif
156 0 : return sal_True;
157 : }
158 0 : return sal_False;
159 : }
160 :
161 : template< class T >
162 0 : void createInstance(
163 : Reference< T > & rxOut,
164 : const Reference< XComponentContext > & xContext,
165 : const OUString & rServiceName )
166 : throw (Exception)
167 : {
168 0 : Reference< XMultiComponentFactory > xMgr( xContext->getServiceManager() );
169 0 : Reference< XInterface > x( xMgr->createInstanceWithContext( rServiceName, xContext ) );
170 :
171 0 : if (! x.is())
172 : {
173 0 : throw RuntimeException(
174 0 : "cannot get service instance \"" + rServiceName + "\"!",
175 0 : Reference< XInterface >() );
176 : }
177 :
178 0 : rxOut = Reference< T >::query( x );
179 0 : if (! rxOut.is())
180 : {
181 0 : const Type & rType = ::getCppuType( (const Reference< T > *)0 );
182 0 : throw RuntimeException(
183 0 : "service instance \"" + rServiceName +
184 0 : "\" does not support demanded interface \"" +
185 0 : rType.getTypeName() + "\"!",
186 0 : Reference< XInterface >() );
187 0 : }
188 0 : }
189 :
190 0 : static Reference< XInterface > loadComponent(
191 : const Reference< XComponentContext > & xContext,
192 : const OUString & rImplName, const OUString & rLocation )
193 : throw (Exception)
194 : {
195 : // determine loader to be used
196 0 : sal_Int32 nDot = rLocation.lastIndexOf( '.' );
197 0 : if (nDot > 0 && nDot < rLocation.getLength())
198 : {
199 0 : Reference< XImplementationLoader > xLoader;
200 :
201 0 : OUString aExt( rLocation.copy( nDot +1 ) );
202 :
203 0 : if (aExt.equalsAscii( "dll" ) ||
204 0 : aExt.equalsAscii( "exe" ) ||
205 0 : aExt.equalsAscii( "dylib" ) ||
206 0 : aExt.equalsAscii( "so" ) )
207 : {
208 : createInstance(
209 0 : xLoader, xContext, OUString("com.sun.star.loader.SharedLibrary") );
210 : }
211 0 : else if (aExt.equalsAscii( "jar" ) ||
212 0 : aExt.equalsAscii( "class" ) )
213 : {
214 : createInstance(
215 0 : xLoader, xContext, OUString("com.sun.star.loader.Java") );
216 : }
217 : else
218 : {
219 : throw RuntimeException(
220 0 : "unknown extension of \"" + rLocation + "\"! No loader available!",
221 0 : Reference< XInterface >() );
222 : }
223 :
224 0 : Reference< XInterface > xInstance;
225 :
226 : // activate
227 0 : Reference< XInterface > xFactory( xLoader->activate(
228 0 : rImplName, OUString(), rLocation, Reference< XRegistryKey >() ) );
229 0 : if (xFactory.is())
230 : {
231 0 : Reference< XSingleComponentFactory > xCFac( xFactory, UNO_QUERY );
232 0 : if (xCFac.is())
233 : {
234 0 : xInstance = xCFac->createInstanceWithContext( xContext );
235 : }
236 : else
237 : {
238 0 : Reference< XSingleServiceFactory > xSFac( xFactory, UNO_QUERY );
239 0 : if (xSFac.is())
240 : {
241 0 : out( "\n> warning: ignroing context for implementation \"" );
242 0 : out( rImplName );
243 0 : out( "\"!" );
244 0 : xInstance = xSFac->createInstance();
245 0 : }
246 0 : }
247 : }
248 :
249 0 : if (! xInstance.is())
250 : {
251 : throw RuntimeException(
252 0 : "activating component \"" + rImplName + "\" from location \"" + rLocation + "\" failed!",
253 0 : Reference< XInterface >() );
254 : }
255 :
256 0 : return xInstance;
257 : }
258 : else
259 : {
260 : throw RuntimeException(
261 0 : "location \"" + rLocation + "\" has no extension! Cannot determine loader to be used!",
262 0 : Reference< XInterface >() );
263 : }
264 : }
265 :
266 0 : class OInstanceProvider
267 : : public WeakImplHelper1< XInstanceProvider >
268 : {
269 : Reference< XComponentContext > _xContext;
270 :
271 : Mutex _aSingleInstanceMutex;
272 : Reference< XInterface > _xSingleInstance;
273 : sal_Bool _bSingleInstance;
274 :
275 : OUString _aImplName;
276 : OUString _aLocation;
277 : OUString _aServiceName;
278 : Sequence< Any > _aInitParams;
279 :
280 : OUString _aInstanceName;
281 :
282 : inline Reference< XInterface > createInstance() throw (Exception);
283 :
284 : public:
285 0 : OInstanceProvider( const Reference< XComponentContext > & xContext,
286 : const OUString & rImplName, const OUString & rLocation,
287 : const OUString & rServiceName, const Sequence< Any > & rInitParams,
288 : sal_Bool bSingleInstance, const OUString & rInstanceName )
289 : : _xContext( xContext )
290 : , _bSingleInstance( bSingleInstance )
291 : , _aImplName( rImplName )
292 : , _aLocation( rLocation )
293 : , _aServiceName( rServiceName )
294 : , _aInitParams( rInitParams )
295 0 : , _aInstanceName( rInstanceName )
296 0 : {}
297 :
298 : // XInstanceProvider
299 : virtual Reference< XInterface > SAL_CALL getInstance( const OUString & rName )
300 : throw (NoSuchElementException, RuntimeException, std::exception) SAL_OVERRIDE;
301 : };
302 :
303 0 : inline Reference< XInterface > OInstanceProvider::createInstance()
304 : throw (Exception)
305 : {
306 0 : Reference< XInterface > xRet;
307 0 : if (!_aImplName.isEmpty()) // manually via loader
308 0 : xRet = loadComponent( _xContext, _aImplName, _aLocation );
309 : else // via service manager
310 0 : unoexe::createInstance( xRet, _xContext, _aServiceName );
311 :
312 : // opt XInit
313 0 : Reference< XInitialization > xInit( xRet, UNO_QUERY );
314 0 : if (xInit.is())
315 0 : xInit->initialize( _aInitParams );
316 :
317 0 : return xRet;
318 : }
319 :
320 0 : Reference< XInterface > OInstanceProvider::getInstance( const OUString & rName )
321 : throw (NoSuchElementException, RuntimeException, std::exception)
322 : {
323 : try
324 : {
325 0 : if (_aInstanceName == rName)
326 : {
327 0 : Reference< XInterface > xRet;
328 :
329 0 : if (_aImplName.isEmpty() && _aServiceName.isEmpty())
330 : {
331 : OSL_ASSERT( rName == "uno.ComponentContext" );
332 0 : xRet = _xContext;
333 : }
334 0 : else if (_bSingleInstance)
335 : {
336 0 : if (! _xSingleInstance.is())
337 : {
338 0 : MutexGuard aGuard( _aSingleInstanceMutex );
339 0 : if (! _xSingleInstance.is())
340 : {
341 0 : _xSingleInstance = createInstance();
342 0 : }
343 : }
344 0 : xRet = _xSingleInstance;
345 : }
346 : else
347 : {
348 0 : xRet = createInstance();
349 : }
350 :
351 0 : return xRet;
352 : }
353 : }
354 0 : catch (Exception & rExc)
355 : {
356 0 : out( "\n> error: " );
357 0 : out( rExc.Message );
358 : }
359 : throw NoSuchElementException(
360 0 : "no such element \"" + rName + "\"!",
361 0 : Reference< XInterface >() );
362 : }
363 :
364 0 : struct ODisposingListener : public WeakImplHelper1< XEventListener >
365 : {
366 : Condition cDisposed;
367 :
368 : // XEventListener
369 : virtual void SAL_CALL disposing( const EventObject & rEvt )
370 : throw (RuntimeException, std::exception) SAL_OVERRIDE;
371 :
372 : static void waitFor( const Reference< XComponent > & xComp );
373 : };
374 :
375 0 : void ODisposingListener::disposing( const EventObject & )
376 : throw (RuntimeException, std::exception)
377 : {
378 0 : cDisposed.set();
379 0 : }
380 :
381 0 : void ODisposingListener::waitFor( const Reference< XComponent > & xComp )
382 : {
383 0 : ODisposingListener * pListener = new ODisposingListener();
384 0 : Reference< XEventListener > xListener( pListener );
385 :
386 0 : xComp->addEventListener( xListener );
387 0 : pListener->cDisposed.wait();
388 0 : }
389 :
390 : } // namespace unoexe
391 :
392 : using namespace unoexe;
393 :
394 0 : SAL_IMPLEMENT_MAIN()
395 : {
396 0 : sal_uInt32 nCount = rtl_getAppCommandArgCount();
397 0 : if (nCount == 0)
398 : {
399 0 : out( arUsingText );
400 0 : return 0;
401 : }
402 :
403 0 : sal_Int32 nRet = 0;
404 0 : Reference< XComponentContext > xContext;
405 :
406 :
407 : try
408 : {
409 0 : OUString aImplName, aLocation, aServiceName, aUnoUrl;
410 0 : Sequence< OUString > aParams;
411 0 : sal_Bool bSingleAccept = sal_False;
412 0 : sal_Bool bSingleInstance = sal_False;
413 :
414 : // read command line arguments
415 :
416 0 : sal_uInt32 nPos = 0;
417 : // read up to arguments
418 0 : while (nPos < nCount)
419 : {
420 0 : OUString arg;
421 :
422 0 : rtl_getAppCommandArg(nPos, &arg.pData);
423 :
424 0 : const OUString dashdash("--");
425 0 : if (dashdash == arg)
426 : {
427 0 : ++nPos;
428 0 : break;
429 : }
430 :
431 0 : if (!(readOption( &aImplName, "c", &nPos, arg) ||
432 0 : readOption( &aLocation, "l", &nPos, arg) ||
433 0 : readOption( &aServiceName, "s", &nPos, arg) ||
434 0 : readOption( &aUnoUrl, "u", &nPos, arg) ||
435 0 : readOption( &s_quiet, "quiet", &nPos, arg) ||
436 0 : readOption( &bSingleAccept, "singleaccept", &nPos, arg) ||
437 0 : readOption( &bSingleInstance, "singleinstance", &nPos, arg)))
438 : {
439 : throw RuntimeException(
440 0 : "unexpected argument \"" + arg + "\"",
441 0 : Reference< XInterface >() );
442 : }
443 0 : }
444 :
445 0 : if (!(aImplName.isEmpty() || aServiceName.isEmpty()))
446 0 : throw RuntimeException("give component exOR service name!", Reference< XInterface >() );
447 0 : if (aImplName.isEmpty() && aServiceName.isEmpty())
448 : {
449 0 : if (! aUnoUrl.endsWithIgnoreAsciiCase( ";uno.ComponentContext" ))
450 : throw RuntimeException(
451 : OUString("expected UNO-URL with instance name uno.ComponentContext!" ),
452 0 : Reference<XInterface>() );
453 0 : if (bSingleInstance)
454 : throw RuntimeException(
455 : OUString("unexpected option --singleinstance!"),
456 0 : Reference<XInterface>() );
457 : }
458 0 : if (!aImplName.isEmpty() && aLocation.isEmpty())
459 0 : throw RuntimeException("give component location!", Reference< XInterface >() );
460 0 : if (!aServiceName.isEmpty() && !aLocation.isEmpty())
461 0 : out( "\n> warning: service name given, will ignore location!" );
462 :
463 : // read component params
464 0 : aParams.realloc( nCount - nPos );
465 0 : OUString * pParams = aParams.getArray();
466 :
467 0 : sal_uInt32 nOffset = nPos;
468 0 : for ( ; nPos < nCount; ++nPos )
469 : {
470 0 : rtl_getAppCommandArg( nPos, &pParams[nPos -nOffset].pData );
471 : }
472 :
473 0 : xContext = defaultBootstrap_InitialComponentContext();
474 :
475 : // accept, instanciate, etc.
476 :
477 0 : if (!aUnoUrl.isEmpty()) // accepting connections
478 : {
479 0 : sal_Int32 nIndex = 0, nTokens = 0;
480 0 : do { aUnoUrl.getToken( 0, ';', nIndex ); nTokens++; } while( nIndex != -1 );
481 0 : if (nTokens != 3 || aUnoUrl.getLength() < 10 ||
482 0 : !aUnoUrl.copy( 0, 4 ).equalsIgnoreAsciiCase( "uno:" ))
483 : {
484 0 : throw RuntimeException("illegal uno url given!", Reference< XInterface >() );
485 : }
486 0 : nIndex = 0;
487 0 : OUString aConnectDescr( aUnoUrl.getToken( 0, ';', nIndex ).copy( 4 ) ); // uno:CONNECTDESCR;iiop;InstanceName
488 0 : OUString aInstanceName( aUnoUrl.getToken( 1, ';', nIndex ) );
489 :
490 0 : Reference< XAcceptor > xAcceptor = Acceptor::create(xContext);
491 :
492 : // init params
493 0 : Sequence< Any > aInitParams( aParams.getLength() );
494 0 : const OUString * p = aParams.getConstArray();
495 0 : Any * pInitParams = aInitParams.getArray();
496 0 : for ( sal_Int32 i = aParams.getLength(); i--; )
497 : {
498 0 : pInitParams[i] = makeAny( p[i] );
499 : }
500 :
501 : // instance provider
502 : Reference< XInstanceProvider > xInstanceProvider( new OInstanceProvider(
503 : xContext, aImplName, aLocation, aServiceName, aInitParams,
504 0 : bSingleInstance, aInstanceName ) );
505 :
506 0 : nIndex = 0;
507 0 : OUString aUnoUrlToken( aUnoUrl.getToken( 1, ';', nIndex ) );
508 : for (;;)
509 : {
510 : // accepting
511 0 : out( "\n> accepting " );
512 0 : out( aConnectDescr );
513 0 : out( "..." );
514 0 : Reference< XConnection > xConnection( xAcceptor->accept( aConnectDescr ) );
515 0 : out( "connection established." );
516 :
517 0 : Reference< XBridgeFactory > xBridgeFactory;
518 : createInstance(
519 : xBridgeFactory, xContext,
520 0 : OUString("com.sun.star.bridge.BridgeFactory") );
521 :
522 : // bridge
523 0 : Reference< XBridge > xBridge( xBridgeFactory->createBridge(
524 : OUString(), aUnoUrlToken,
525 0 : xConnection, xInstanceProvider ) );
526 :
527 0 : if (bSingleAccept)
528 : {
529 0 : Reference< XComponent > xComp( xBridge, UNO_QUERY );
530 0 : if (! xComp.is())
531 0 : throw RuntimeException( OUString( "bridge factory does not export interface \"com.sun.star.lang.XComponent\"!" ), Reference< XInterface >() );
532 0 : ODisposingListener::waitFor( xComp );
533 0 : xComp->dispose();
534 : // explicitly dispose the remote bridge so that it joins
535 : // on all spawned threads before process exit (see
536 : // binaryurp/source/bridge.cxx for details)
537 0 : break;
538 : }
539 0 : }
540 : }
541 : else // no uno url
542 : {
543 0 : Reference< XInterface > xInstance;
544 0 : if (!aImplName.isEmpty()) // manually via loader
545 0 : xInstance = loadComponent( xContext, aImplName, aLocation );
546 : else // via service manager
547 0 : createInstance( xInstance, xContext, aServiceName );
548 :
549 : // execution
550 0 : Reference< XMain > xMain( xInstance, UNO_QUERY );
551 0 : if (xMain.is())
552 : {
553 0 : nRet = xMain->run( aParams );
554 : }
555 : else
556 : {
557 0 : Reference< XComponent > xComp( xInstance, UNO_QUERY );
558 0 : if (xComp.is())
559 0 : xComp->dispose();
560 0 : throw RuntimeException( OUString( "component does not export interface interface \"com.sun.star.lang.XMain\"!" ), Reference< XInterface >() );
561 0 : }
562 0 : }
563 : }
564 0 : catch (Exception & rExc)
565 : {
566 0 : out( "\n> error: " );
567 0 : out( rExc.Message );
568 0 : out( "\n> dying..." );
569 0 : nRet = 1;
570 : }
571 :
572 : // cleanup
573 0 : Reference< XComponent > xComp( xContext, UNO_QUERY );
574 0 : if (xComp.is())
575 0 : xComp->dispose();
576 :
577 : #if OSL_DEBUG_LEVEL > 1
578 : out( "\n" );
579 : #endif
580 0 : return nRet;
581 : }
582 :
583 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|