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