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