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