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