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 <config_features.h>
21 : #include <config_version.h>
22 :
23 : #include "app.hxx"
24 : #include "officeipcthread.hxx"
25 : #include "cmdlineargs.hxx"
26 : #include "dispatchwatcher.hxx"
27 : #include <boost/scoped_ptr.hpp>
28 : #include <stdio.h>
29 : #include <osl/process.h>
30 : #include <unotools/bootstrap.hxx>
31 : #include <vcl/svapp.hxx>
32 : #include <vcl/help.hxx>
33 : #include <unotools/configmgr.hxx>
34 : #include <osl/thread.hxx>
35 : #include <rtl/digest.h>
36 : #include <rtl/ustrbuf.hxx>
37 : #include <rtl/instance.hxx>
38 : #include <osl/conditn.hxx>
39 : #include <unotools/moduleoptions.hxx>
40 : #include <rtl/bootstrap.hxx>
41 : #include <rtl/strbuf.hxx>
42 : #include <comphelper/processfactory.hxx>
43 : #include <cppuhelper/supportsservice.hxx>
44 : #include <osl/file.hxx>
45 : #include <rtl/process.h>
46 : #include <tools/getprocessworkingdir.hxx>
47 : #include <boost/scoped_array.hpp>
48 :
49 : using namespace desktop;
50 : using namespace ::com::sun::star::uno;
51 : using namespace ::com::sun::star::lang;
52 : using namespace ::com::sun::star::frame;
53 :
54 :
55 : const char *OfficeIPCThread::sc_aShowSequence = "-tofront";
56 : const int OfficeIPCThread::sc_nShSeqLength = 5;
57 :
58 : namespace {
59 :
60 : #if HAVE_FEATURE_DESKTOP
61 :
62 : static char const ARGUMENT_PREFIX[] = "InternalIPC::Arguments";
63 : static char const SEND_ARGUMENTS[] = "InternalIPC::SendArguments";
64 : static char const PROCESSING_DONE[] = "InternalIPC::ProcessingDone";
65 :
66 : // Receives packets from the pipe until a packet ends in a NUL character (that
67 : // will not be included in the returned string) or it cannot read anything (due
68 : // to error or closed pipe, in which case an empty string will be returned to
69 : // signal failure):
70 0 : OString readStringFromPipe(osl::StreamPipe & pipe) {
71 0 : for (OStringBuffer str;;) {
72 : char buf[1024];
73 0 : sal_Int32 n = pipe.recv(buf, SAL_N_ELEMENTS(buf));
74 0 : if (n <= 0) {
75 0 : return "";
76 : }
77 0 : bool end = false;
78 0 : if (buf[n - 1] == '\0') {
79 0 : end = true;
80 0 : --n;
81 : }
82 0 : str.append(buf, n);
83 : //TODO: how does OStringBuffer.append handle overflow?
84 0 : if (end) {
85 0 : return str.makeStringAndClear();
86 : }
87 0 : }
88 : }
89 :
90 : #endif
91 :
92 : }
93 :
94 : // Type of pipe we use
95 : enum PipeMode
96 : {
97 : PIPEMODE_DONTKNOW,
98 : PIPEMODE_CREATED,
99 : PIPEMODE_CONNECTED
100 : };
101 :
102 : namespace desktop
103 : {
104 :
105 : namespace {
106 :
107 : #if HAVE_FEATURE_DESKTOP
108 :
109 : class Parser: public CommandLineArgs::Supplier {
110 : public:
111 0 : explicit Parser(OString const & input): m_input(input) {
112 0 : if (!m_input.match(ARGUMENT_PREFIX) ||
113 0 : m_input.getLength() == RTL_CONSTASCII_LENGTH(ARGUMENT_PREFIX))
114 : {
115 0 : throw CommandLineArgs::Supplier::Exception();
116 : }
117 0 : m_index = RTL_CONSTASCII_LENGTH(ARGUMENT_PREFIX);
118 0 : switch (m_input[m_index++]) {
119 : case '0':
120 0 : break;
121 : case '1':
122 : {
123 0 : OUString url;
124 0 : if (!next(&url, false)) {
125 0 : throw CommandLineArgs::Supplier::Exception();
126 : }
127 0 : m_cwdUrl.reset(url);
128 0 : break;
129 : }
130 : case '2':
131 : {
132 0 : OUString path;
133 0 : if (!next(&path, false)) {
134 0 : throw CommandLineArgs::Supplier::Exception();
135 : }
136 0 : OUString url;
137 0 : if (osl::FileBase::getFileURLFromSystemPath(path, url) ==
138 : osl::FileBase::E_None)
139 : {
140 0 : m_cwdUrl.reset(url);
141 : }
142 0 : break;
143 : }
144 : default:
145 0 : throw CommandLineArgs::Supplier::Exception();
146 : }
147 0 : }
148 :
149 0 : virtual ~Parser() {}
150 :
151 0 : virtual boost::optional< OUString > getCwdUrl() SAL_OVERRIDE { return m_cwdUrl; }
152 :
153 0 : virtual bool next(OUString * argument) SAL_OVERRIDE { return next(argument, true); }
154 :
155 : private:
156 0 : virtual bool next(OUString * argument, bool prefix) {
157 : OSL_ASSERT(argument != NULL);
158 0 : if (m_index < m_input.getLength()) {
159 0 : if (prefix) {
160 0 : if (m_input[m_index] != ',') {
161 0 : throw CommandLineArgs::Supplier::Exception();
162 : }
163 0 : ++m_index;
164 : }
165 0 : OStringBuffer b;
166 0 : while (m_index < m_input.getLength()) {
167 0 : char c = m_input[m_index];
168 0 : if (c == ',') {
169 0 : break;
170 : }
171 0 : ++m_index;
172 0 : if (c == '\\') {
173 0 : if (m_index < m_input.getLength()) {
174 0 : c = m_input[m_index++];
175 0 : switch (c) {
176 : case '0':
177 0 : c = '\0';
178 0 : break;
179 : case ',':
180 : case '\\':
181 0 : break;
182 : default:
183 0 : throw CommandLineArgs::Supplier::Exception();
184 : }
185 : } else {
186 0 : throw CommandLineArgs::Supplier::Exception();
187 : }
188 : }
189 0 : b.append(c);
190 : }
191 0 : OString b2(b.makeStringAndClear());
192 0 : if (!rtl_convertStringToUString(
193 : &argument->pData, b2.getStr(), b2.getLength(),
194 : RTL_TEXTENCODING_UTF8,
195 : (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR |
196 : RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR |
197 0 : RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
198 : {
199 0 : throw CommandLineArgs::Supplier::Exception();
200 : }
201 0 : return true;
202 : } else {
203 0 : return false;
204 : }
205 : }
206 :
207 : boost::optional< OUString > m_cwdUrl;
208 : OString m_input;
209 : sal_Int32 m_index;
210 : };
211 :
212 0 : bool addArgument(OStringBuffer &rArguments, char prefix,
213 : const OUString &rArgument)
214 : {
215 0 : OString utf8;
216 0 : if (!rArgument.convertToString(
217 : &utf8, RTL_TEXTENCODING_UTF8,
218 : (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
219 0 : RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
220 : {
221 0 : return false;
222 : }
223 0 : rArguments.append(prefix);
224 0 : for (sal_Int32 i = 0; i < utf8.getLength(); ++i) {
225 0 : char c = utf8[i];
226 0 : switch (c) {
227 : case '\0':
228 0 : rArguments.append("\\0");
229 0 : break;
230 : case ',':
231 0 : rArguments.append("\\,");
232 0 : break;
233 : case '\\':
234 0 : rArguments.append("\\\\");
235 0 : break;
236 : default:
237 0 : rArguments.append(c);
238 0 : break;
239 : }
240 : }
241 0 : return true;
242 : }
243 :
244 : #endif
245 :
246 : }
247 :
248 160 : rtl::Reference< OfficeIPCThread > OfficeIPCThread::pGlobalOfficeIPCThread;
249 : namespace { struct Security : public rtl::Static<osl::Security, Security> {}; }
250 :
251 : // Turns a string in aMsg such as file:///home/foo/.libreoffice/3
252 : // Into a hex string of well known length ff132a86...
253 160 : OUString CreateMD5FromString( const OUString& aMsg )
254 : {
255 : #if (OSL_DEBUG_LEVEL > 2)
256 : fprintf( stderr, "create md5 from '%s'\n",
257 : OUStringToOString (aMsg, RTL_TEXTENCODING_UTF8).getStr() );
258 : #endif
259 :
260 160 : rtlDigest handle = rtl_digest_create( rtl_Digest_AlgorithmMD5 );
261 160 : if ( handle )
262 : {
263 160 : const sal_uInt8* pData = (const sal_uInt8*)aMsg.getStr();
264 160 : sal_uInt32 nSize = ( aMsg.getLength() * sizeof( sal_Unicode ));
265 160 : sal_uInt32 nMD5KeyLen = rtl_digest_queryLength( handle );
266 160 : boost::scoped_array<sal_uInt8> pMD5KeyBuffer(new sal_uInt8[ nMD5KeyLen ]);
267 :
268 160 : rtl_digest_init( handle, pData, nSize );
269 160 : rtl_digest_update( handle, pData, nSize );
270 160 : rtl_digest_get( handle, pMD5KeyBuffer.get(), nMD5KeyLen );
271 160 : rtl_digest_destroy( handle );
272 :
273 : // Create hex-value string from the MD5 value to keep the string size minimal
274 320 : OUStringBuffer aBuffer( nMD5KeyLen * 2 + 1 );
275 2720 : for ( sal_uInt32 i = 0; i < nMD5KeyLen; i++ )
276 2560 : aBuffer.append( (sal_Int32)pMD5KeyBuffer[i], 16 );
277 :
278 320 : return aBuffer.makeStringAndClear();
279 : }
280 :
281 0 : return OUString();
282 : }
283 :
284 : class ProcessEventsClass_Impl
285 : {
286 : public:
287 : DECL_STATIC_LINK( ProcessEventsClass_Impl, CallEvent, void* pEvent );
288 : DECL_STATIC_LINK( ProcessEventsClass_Impl, ProcessDocumentsEvent, void* pEvent );
289 : };
290 :
291 0 : IMPL_STATIC_LINK_NOINSTANCE( ProcessEventsClass_Impl, CallEvent, void*, pEvent )
292 : {
293 : // Application events are processed by the Desktop::HandleAppEvent implementation.
294 0 : Desktop::HandleAppEvent( *((ApplicationEvent*)pEvent) );
295 0 : delete (ApplicationEvent*)pEvent;
296 0 : return 0;
297 : }
298 :
299 0 : IMPL_STATIC_LINK_NOINSTANCE( ProcessEventsClass_Impl, ProcessDocumentsEvent, void*, pEvent )
300 : {
301 : // Documents requests are processed by the OfficeIPCThread implementation
302 0 : ProcessDocumentsRequest* pDocsRequest = (ProcessDocumentsRequest*)pEvent;
303 :
304 0 : if ( pDocsRequest )
305 : {
306 0 : OfficeIPCThread::ExecuteCmdLineRequests( *pDocsRequest );
307 0 : delete pDocsRequest;
308 : }
309 0 : return 0;
310 : }
311 :
312 0 : void ImplPostForeignAppEvent( ApplicationEvent* pEvent )
313 : {
314 0 : Application::PostUserEvent( STATIC_LINK( NULL, ProcessEventsClass_Impl, CallEvent ), pEvent );
315 0 : }
316 :
317 0 : void ImplPostProcessDocumentsEvent( ProcessDocumentsRequest* pEvent )
318 : {
319 0 : Application::PostUserEvent( STATIC_LINK( NULL, ProcessEventsClass_Impl, ProcessDocumentsEvent ), pEvent );
320 0 : }
321 :
322 0 : oslSignalAction SAL_CALL SalMainPipeExchangeSignal_impl(void* /*pData*/, oslSignalInfo* pInfo)
323 : {
324 0 : if( pInfo->Signal == osl_Signal_Terminate )
325 0 : OfficeIPCThread::DisableOfficeIPCThread(false);
326 0 : return osl_Signal_ActCallNextHdl;
327 : }
328 :
329 :
330 :
331 : // The OfficeIPCThreadController implementation is a bookkeeper for all pending requests
332 : // that were created by the OfficeIPCThread. The requests are waiting to be processed by
333 : // our framework loadComponentFromURL function (e.g. open/print request).
334 : // During shutdown the framework is asking OfficeIPCThreadController about pending requests.
335 : // If there are pending requests framework has to stop the shutdown process. It is waiting
336 : // for these requests because framework is not able to handle shutdown and open a document
337 : // concurrently.
338 :
339 :
340 : // XServiceInfo
341 96 : OUString SAL_CALL OfficeIPCThreadController::getImplementationName()
342 : throw ( RuntimeException, std::exception )
343 : {
344 96 : return OUString( "com.sun.star.comp.OfficeIPCThreadController" );
345 : }
346 :
347 0 : sal_Bool OfficeIPCThreadController::supportsService(
348 : OUString const & ServiceName) throw (css::uno::RuntimeException, std::exception)
349 : {
350 0 : return cppu::supportsService(this, ServiceName);
351 : }
352 :
353 0 : Sequence< OUString > SAL_CALL OfficeIPCThreadController::getSupportedServiceNames()
354 : throw ( RuntimeException, std::exception )
355 : {
356 0 : Sequence< OUString > aSeq( 0 );
357 0 : return aSeq;
358 : }
359 :
360 : // XEventListener
361 0 : void SAL_CALL OfficeIPCThreadController::disposing( const EventObject& )
362 : throw( RuntimeException, std::exception )
363 : {
364 0 : }
365 :
366 : // XTerminateListener
367 96 : void SAL_CALL OfficeIPCThreadController::queryTermination( const EventObject& )
368 : throw( TerminationVetoException, RuntimeException, std::exception )
369 : {
370 : // Desktop ask about pending request through our office ipc pipe. We have to
371 : // be sure that no pending request is waiting because framework is not able to
372 : // handle shutdown and open a document concurrently.
373 :
374 96 : if ( OfficeIPCThread::AreRequestsPending() )
375 0 : throw TerminationVetoException();
376 : else
377 96 : OfficeIPCThread::SetDowning();
378 96 : }
379 :
380 96 : void SAL_CALL OfficeIPCThreadController::notifyTermination( const EventObject& )
381 : throw( RuntimeException, std::exception )
382 : {
383 96 : }
384 :
385 : namespace
386 : {
387 : class theOfficeIPCThreadMutex
388 : : public rtl::Static<osl::Mutex, theOfficeIPCThreadMutex> {};
389 : }
390 :
391 864 : ::osl::Mutex& OfficeIPCThread::GetMutex()
392 : {
393 864 : return theOfficeIPCThreadMutex::get();
394 : }
395 :
396 96 : void OfficeIPCThread::SetDowning()
397 : {
398 : // We have the order to block all incoming requests. Framework
399 : // wants to shutdown and we have to make sure that no loading/printing
400 : // requests are executed anymore.
401 96 : ::osl::MutexGuard aGuard( GetMutex() );
402 :
403 96 : if ( pGlobalOfficeIPCThread.is() )
404 96 : pGlobalOfficeIPCThread->mbDowning = true;
405 96 : }
406 :
407 : static bool s_bInEnableRequests = false;
408 :
409 96 : void OfficeIPCThread::EnableRequests( bool i_bEnable )
410 : {
411 : // switch between just queueing the requests and executing them
412 96 : ::osl::MutexGuard aGuard( GetMutex() );
413 :
414 96 : if ( pGlobalOfficeIPCThread.is() )
415 : {
416 96 : s_bInEnableRequests = true;
417 96 : pGlobalOfficeIPCThread->mbRequestsEnabled = i_bEnable;
418 96 : if( i_bEnable )
419 : {
420 : // hit the compiler over the head
421 96 : ProcessDocumentsRequest aEmptyReq = ProcessDocumentsRequest( boost::optional< OUString >() );
422 : // trigger already queued requests
423 96 : OfficeIPCThread::ExecuteCmdLineRequests( aEmptyReq );
424 : }
425 96 : s_bInEnableRequests = false;
426 96 : }
427 96 : }
428 :
429 96 : bool OfficeIPCThread::AreRequestsPending()
430 : {
431 : // Give info about pending requests
432 96 : ::osl::MutexGuard aGuard( GetMutex() );
433 96 : if ( pGlobalOfficeIPCThread.is() )
434 96 : return ( pGlobalOfficeIPCThread->mnPendingRequests > 0 );
435 : else
436 0 : return false;
437 : }
438 :
439 0 : void OfficeIPCThread::RequestsCompleted( int nCount )
440 : {
441 : // Remove nCount pending requests from our internal counter
442 0 : ::osl::MutexGuard aGuard( GetMutex() );
443 0 : if ( pGlobalOfficeIPCThread.is() )
444 : {
445 0 : if ( pGlobalOfficeIPCThread->mnPendingRequests > 0 )
446 0 : pGlobalOfficeIPCThread->mnPendingRequests -= nCount;
447 0 : }
448 0 : }
449 :
450 160 : OfficeIPCThread::Status OfficeIPCThread::EnableOfficeIPCThread()
451 : {
452 : #if HAVE_FEATURE_DESKTOP
453 160 : ::osl::MutexGuard aGuard( GetMutex() );
454 :
455 160 : if( pGlobalOfficeIPCThread.is() )
456 0 : return IPC_STATUS_OK;
457 :
458 320 : OUString aUserInstallPath;
459 320 : OUString aDummy;
460 :
461 320 : rtl::Reference< OfficeIPCThread > pThread(new OfficeIPCThread);
462 :
463 : // The name of the named pipe is created with the hashcode of the user installation directory (without /user). We have to retrieve
464 : // this information from a unotools implementation.
465 160 : ::utl::Bootstrap::PathStatus aLocateResult = ::utl::Bootstrap::locateUserInstallation( aUserInstallPath );
466 160 : if ( aLocateResult == ::utl::Bootstrap::PATH_EXISTS || aLocateResult == ::utl::Bootstrap::PATH_VALID)
467 160 : aDummy = aUserInstallPath;
468 : else
469 : {
470 0 : return IPC_STATUS_BOOTSTRAP_ERROR;
471 : }
472 :
473 : // Try to determine if we are the first office or not! This should prevent multiple
474 : // access to the user directory !
475 : // First we try to create our pipe if this fails we try to connect. We have to do this
476 : // in a loop because the other office can crash or shutdown between createPipe
477 : // and connectPipe!!
478 :
479 320 : OUString aIniName;
480 :
481 160 : osl_getExecutableFile( &aIniName.pData );
482 :
483 160 : sal_uInt32 lastIndex = aIniName.lastIndexOf('/');
484 160 : if ( lastIndex > 0 )
485 : {
486 160 : aIniName = aIniName.copy( 0, lastIndex+1 );
487 160 : aIniName += "perftune";
488 : #if defined(WNT)
489 : aIniName += ".ini";
490 : #else
491 160 : aIniName += "rc";
492 : #endif
493 : }
494 :
495 320 : ::rtl::Bootstrap aPerfTuneIniFile( aIniName );
496 :
497 320 : OUString aDefault( "0" );
498 320 : OUString aPreloadData;
499 :
500 160 : aPerfTuneIniFile.getFrom( OUString( "FastPipeCommunication" ), aPreloadData, aDefault );
501 :
502 :
503 320 : OUString aUserInstallPathHashCode;
504 :
505 160 : if ( aPreloadData == "1" )
506 : {
507 : sal_Char szBuffer[32];
508 0 : sprintf( szBuffer, "%d", LIBO_VERSION_MAJOR * 10000 + LIBO_VERSION_MINOR * 100 + LIBO_VERSION_MICRO * 1 );
509 0 : aUserInstallPathHashCode = OUString( szBuffer, strlen(szBuffer), osl_getThreadTextEncoding() );
510 : }
511 : else
512 160 : aUserInstallPathHashCode = CreateMD5FromString( aDummy );
513 :
514 :
515 : // Check result to create a hash code from the user install path
516 160 : if ( aUserInstallPathHashCode.isEmpty() )
517 0 : return IPC_STATUS_BOOTSTRAP_ERROR; // Something completely broken, we cannot create a valid hash code!
518 :
519 320 : OUString aPipeIdent( "SingleOfficeIPC_" + aUserInstallPathHashCode );
520 :
521 160 : PipeMode nPipeMode = PIPEMODE_DONTKNOW;
522 160 : do
523 : {
524 160 : osl::Security &rSecurity = Security::get();
525 :
526 : // Try to create pipe
527 160 : if ( pThread->maPipe.create( aPipeIdent.getStr(), osl_Pipe_CREATE, rSecurity ))
528 : {
529 : // Pipe created
530 160 : nPipeMode = PIPEMODE_CREATED;
531 : }
532 0 : else if( pThread->maPipe.create( aPipeIdent.getStr(), osl_Pipe_OPEN, rSecurity )) // Creation not successful, now we try to connect
533 : {
534 0 : osl::StreamPipe aStreamPipe(pThread->maPipe.getHandle());
535 0 : if (readStringFromPipe(aStreamPipe) == SEND_ARGUMENTS)
536 : {
537 : // Pipe connected to first office
538 0 : nPipeMode = PIPEMODE_CONNECTED;
539 : }
540 : else
541 : {
542 : // Pipe connection failed (other office exited or crashed)
543 : TimeValue tval;
544 0 : tval.Seconds = 0;
545 0 : tval.Nanosec = 500000000;
546 0 : salhelper::Thread::wait( tval );
547 0 : }
548 : }
549 : else
550 : {
551 0 : oslPipeError eReason = pThread->maPipe.getError();
552 0 : if ((eReason == osl_Pipe_E_ConnectionRefused) || (eReason == osl_Pipe_E_invalidError))
553 0 : return IPC_STATUS_PIPE_ERROR;
554 :
555 : // Wait for second office to be ready
556 : TimeValue aTimeValue;
557 0 : aTimeValue.Seconds = 0;
558 0 : aTimeValue.Nanosec = 10000000; // 10ms
559 0 : salhelper::Thread::wait( aTimeValue );
560 : }
561 :
562 : } while ( nPipeMode == PIPEMODE_DONTKNOW );
563 :
564 160 : if ( nPipeMode == PIPEMODE_CREATED )
565 : {
566 : // Seems we are the one and only, so start listening thread
567 160 : pGlobalOfficeIPCThread = pThread;
568 160 : pThread->launch();
569 : }
570 : else
571 : {
572 : // Seems another office is running. Pipe arguments to it and self terminate
573 0 : osl::StreamPipe aStreamPipe(pThread->maPipe.getHandle());
574 :
575 0 : OStringBuffer aArguments(ARGUMENT_PREFIX);
576 0 : OUString cwdUrl;
577 0 : if (!(tools::getProcessWorkingDir(cwdUrl) &&
578 0 : addArgument(aArguments, '1', cwdUrl)))
579 : {
580 0 : aArguments.append('0');
581 : }
582 0 : sal_uInt32 nCount = rtl_getAppCommandArgCount();
583 0 : for( sal_uInt32 i=0; i < nCount; i++ )
584 : {
585 0 : rtl_getAppCommandArg( i, &aDummy.pData );
586 0 : if (!addArgument(aArguments, ',', aDummy)) {
587 0 : return IPC_STATUS_BOOTSTRAP_ERROR;
588 : }
589 : }
590 0 : aArguments.append('\0');
591 : // finally, write the string onto the pipe
592 : sal_Int32 n = aStreamPipe.write(
593 0 : aArguments.getStr(), aArguments.getLength());
594 0 : if (n != aArguments.getLength()) {
595 : SAL_INFO("desktop", "short write: " << n);
596 0 : return IPC_STATUS_BOOTSTRAP_ERROR;
597 : }
598 :
599 0 : if (readStringFromPipe(aStreamPipe) != PROCESSING_DONE)
600 : {
601 : // something went wrong
602 0 : return IPC_STATUS_BOOTSTRAP_ERROR;
603 : }
604 :
605 0 : return IPC_STATUS_2ND_OFFICE;
606 : }
607 : #else
608 : pGlobalOfficeIPCThread = rtl::Reference< OfficeIPCThread >(new OfficeIPCThread);
609 : #endif
610 320 : return IPC_STATUS_OK;
611 : }
612 :
613 160 : void OfficeIPCThread::DisableOfficeIPCThread(bool join)
614 : {
615 : #if HAVE_FEATURE_DESKTOP
616 160 : osl::ClearableMutexGuard aMutex( GetMutex() );
617 :
618 160 : if( pGlobalOfficeIPCThread.is() )
619 : {
620 : rtl::Reference< OfficeIPCThread > pOfficeIPCThread(
621 160 : pGlobalOfficeIPCThread);
622 160 : pGlobalOfficeIPCThread.clear();
623 :
624 160 : pOfficeIPCThread->mbDowning = true;
625 160 : pOfficeIPCThread->maPipe.close();
626 :
627 : // release mutex to avoid deadlocks
628 160 : aMutex.clear();
629 :
630 160 : OfficeIPCThread::SetReady(pOfficeIPCThread);
631 :
632 : // exit gracefully and join
633 160 : if (join)
634 : {
635 160 : pOfficeIPCThread->join();
636 160 : }
637 160 : }
638 : #else
639 : (void) join;
640 : #endif
641 160 : }
642 :
643 160 : OfficeIPCThread::OfficeIPCThread() :
644 : Thread( "OfficeIPCThread" ),
645 : mbDowning( false ),
646 : mbRequestsEnabled( false ),
647 : mnPendingRequests( 0 ),
648 160 : mpDispatchWatcher( 0 )
649 : {
650 160 : }
651 :
652 480 : OfficeIPCThread::~OfficeIPCThread()
653 : {
654 160 : ::osl::ClearableMutexGuard aGuard( GetMutex() );
655 :
656 160 : if ( mpDispatchWatcher )
657 96 : mpDispatchWatcher->release();
658 160 : maPipe.close();
659 160 : pGlobalOfficeIPCThread.clear();
660 320 : }
661 :
662 256 : void OfficeIPCThread::SetReady(
663 : rtl::Reference< OfficeIPCThread > const & pThread)
664 : {
665 : rtl::Reference< OfficeIPCThread > const & t(
666 256 : pThread.is() ? pThread : pGlobalOfficeIPCThread);
667 256 : if (t.is())
668 : {
669 256 : t->cReady.set();
670 : }
671 256 : }
672 :
673 160 : void OfficeIPCThread::execute()
674 : {
675 : #if HAVE_FEATURE_DESKTOP
676 0 : do
677 : {
678 160 : osl::StreamPipe aStreamPipe;
679 160 : oslPipeError nError = maPipe.accept( aStreamPipe );
680 :
681 :
682 160 : if( nError == osl_Pipe_E_None )
683 : {
684 : // if we receive a request while the office is displaying some dialog or error during
685 : // bootstrap, that dialogs event loop might get events that are dispatched by this thread
686 : // we have to wait for cReady to be set by the real main loop.
687 : // only reqests that dont dispatch events may be processed before cReady is set.
688 160 : cReady.wait();
689 :
690 : // we might have decided to shutdown while we were sleeping
691 320 : if (!pGlobalOfficeIPCThread.is()) return;
692 :
693 : // only lock the mutex when processing starts, othewise we deadlock when the office goes
694 : // down during wait
695 0 : osl::ClearableMutexGuard aGuard( GetMutex() );
696 :
697 0 : if ( mbDowning )
698 : {
699 0 : break;
700 : }
701 :
702 : // notify client we're ready to process its args:
703 : sal_Int32 n = aStreamPipe.write(
704 0 : SEND_ARGUMENTS, SAL_N_ELEMENTS(SEND_ARGUMENTS));
705 : // incl. terminating NUL
706 0 : if (n != SAL_N_ELEMENTS(SEND_ARGUMENTS)) {
707 : SAL_WARN("desktop", "short write: " << n);
708 0 : continue;
709 : }
710 :
711 0 : OString aArguments = readStringFromPipe(aStreamPipe);
712 :
713 : // Is this a lookup message from another application? if so, ignore
714 0 : if (aArguments.isEmpty())
715 0 : continue;
716 :
717 0 : boost::scoped_ptr< CommandLineArgs > aCmdLineArgs;
718 : try
719 : {
720 0 : Parser p(aArguments);
721 0 : aCmdLineArgs.reset( new CommandLineArgs( p ) );
722 : }
723 0 : catch ( const CommandLineArgs::Supplier::Exception & )
724 : {
725 : #if (OSL_DEBUG_LEVEL > 1) || defined DBG_UTIL
726 : fprintf( stderr, "Error in received command line arguments\n" );
727 : #endif
728 0 : continue;
729 : }
730 :
731 0 : bool bDocRequestSent = false;
732 :
733 0 : OUString aUnknown( aCmdLineArgs->GetUnknown() );
734 0 : if ( !aUnknown.isEmpty() || aCmdLineArgs->IsHelp() )
735 : {
736 : ApplicationEvent* pAppEvent =
737 0 : new ApplicationEvent(ApplicationEvent::TYPE_HELP, aUnknown);
738 0 : ImplPostForeignAppEvent( pAppEvent );
739 : }
740 0 : else if ( aCmdLineArgs->IsVersion() )
741 : {
742 : ApplicationEvent* pAppEvent =
743 0 : new ApplicationEvent(ApplicationEvent::TYPE_VERSION);
744 0 : ImplPostForeignAppEvent( pAppEvent );
745 : }
746 : else
747 : {
748 0 : const CommandLineArgs &rCurrentCmdLineArgs = Desktop::GetCommandLineArgs();
749 :
750 0 : if ( aCmdLineArgs->IsQuickstart() )
751 : {
752 : // we have to use application event, because we have to start quickstart service in main thread!!
753 : ApplicationEvent* pAppEvent =
754 0 : new ApplicationEvent(ApplicationEvent::TYPE_QUICKSTART);
755 0 : ImplPostForeignAppEvent( pAppEvent );
756 : }
757 :
758 : // handle request for acceptor
759 : std::vector< OUString > const & accept = aCmdLineArgs->
760 0 : GetAccept();
761 0 : for (std::vector< OUString >::const_iterator i(accept.begin());
762 0 : i != accept.end(); ++i)
763 : {
764 : ApplicationEvent* pAppEvent = new ApplicationEvent(
765 0 : ApplicationEvent::TYPE_ACCEPT, *i);
766 0 : ImplPostForeignAppEvent( pAppEvent );
767 : }
768 : // handle acceptor removal
769 : std::vector< OUString > const & unaccept = aCmdLineArgs->
770 0 : GetUnaccept();
771 0 : for (std::vector< OUString >::const_iterator i(
772 0 : unaccept.begin());
773 0 : i != unaccept.end(); ++i)
774 : {
775 : ApplicationEvent* pAppEvent = new ApplicationEvent(
776 0 : ApplicationEvent::TYPE_UNACCEPT, *i);
777 0 : ImplPostForeignAppEvent( pAppEvent );
778 : }
779 :
780 : ProcessDocumentsRequest* pRequest = new ProcessDocumentsRequest(
781 0 : aCmdLineArgs->getCwdUrl());
782 0 : cProcessed.reset();
783 0 : pRequest->pcProcessed = &cProcessed;
784 :
785 : // Print requests are not dependent on the --invisible cmdline argument as they are
786 : // loaded with the "hidden" flag! So they are always checked.
787 0 : pRequest->aPrintList = aCmdLineArgs->GetPrintList();
788 0 : bDocRequestSent |= !pRequest->aPrintList.empty();
789 0 : pRequest->aPrintToList = aCmdLineArgs->GetPrintToList();
790 0 : pRequest->aPrinterName = aCmdLineArgs->GetPrinterName();
791 0 : bDocRequestSent |= !( pRequest->aPrintToList.empty() || pRequest->aPrinterName.isEmpty() );
792 :
793 0 : if ( !rCurrentCmdLineArgs.IsInvisible() )
794 : {
795 : // Read cmdline args that can open/create documents. As they would open a window
796 : // they are only allowed if the "--invisible" is currently not used!
797 0 : pRequest->aOpenList = aCmdLineArgs->GetOpenList();
798 0 : bDocRequestSent |= !pRequest->aOpenList.empty();
799 0 : pRequest->aViewList = aCmdLineArgs->GetViewList();
800 0 : bDocRequestSent |= !pRequest->aViewList.empty();
801 0 : pRequest->aStartList = aCmdLineArgs->GetStartList();
802 0 : bDocRequestSent |= !pRequest->aStartList.empty();
803 0 : pRequest->aForceOpenList = aCmdLineArgs->GetForceOpenList();
804 0 : bDocRequestSent |= !pRequest->aForceOpenList.empty();
805 0 : pRequest->aForceNewList = aCmdLineArgs->GetForceNewList();
806 0 : bDocRequestSent |= !pRequest->aForceNewList.empty();
807 :
808 : // Special command line args to create an empty document for a given module
809 :
810 : // #i18338# (lo)
811 : // we only do this if no document was specified on the command line,
812 : // since this would be inconsistent with the behaviour of
813 : // the first process, see OpenClients() (call to OpenDefault()) in app.cxx
814 0 : if ( aCmdLineArgs->HasModuleParam() && !bDocRequestSent )
815 : {
816 0 : SvtModuleOptions aOpt;
817 0 : SvtModuleOptions::EFactory eFactory = SvtModuleOptions::E_WRITER;
818 0 : if ( aCmdLineArgs->IsWriter() )
819 0 : eFactory = SvtModuleOptions::E_WRITER;
820 0 : else if ( aCmdLineArgs->IsCalc() )
821 0 : eFactory = SvtModuleOptions::E_CALC;
822 0 : else if ( aCmdLineArgs->IsDraw() )
823 0 : eFactory = SvtModuleOptions::E_DRAW;
824 0 : else if ( aCmdLineArgs->IsImpress() )
825 0 : eFactory = SvtModuleOptions::E_IMPRESS;
826 0 : else if ( aCmdLineArgs->IsBase() )
827 0 : eFactory = SvtModuleOptions::E_DATABASE;
828 0 : else if ( aCmdLineArgs->IsMath() )
829 0 : eFactory = SvtModuleOptions::E_MATH;
830 0 : else if ( aCmdLineArgs->IsGlobal() )
831 0 : eFactory = SvtModuleOptions::E_WRITERGLOBAL;
832 0 : else if ( aCmdLineArgs->IsWeb() )
833 0 : eFactory = SvtModuleOptions::E_WRITERWEB;
834 :
835 0 : if ( !pRequest->aOpenList.empty() )
836 0 : pRequest->aModule = aOpt.GetFactoryName( eFactory );
837 : else
838 0 : pRequest->aOpenList.push_back( aOpt.GetFactoryEmptyDocumentURL( eFactory ) );
839 0 : bDocRequestSent = true;
840 : }
841 : }
842 :
843 0 : if ( !aCmdLineArgs->IsQuickstart() ) {
844 0 : bool bShowHelp = false;
845 0 : OUStringBuffer aHelpURLBuffer;
846 0 : if (aCmdLineArgs->IsHelpWriter()) {
847 0 : bShowHelp = true;
848 0 : aHelpURLBuffer.appendAscii("vnd.sun.star.help://swriter/start");
849 0 : } else if (aCmdLineArgs->IsHelpCalc()) {
850 0 : bShowHelp = true;
851 0 : aHelpURLBuffer.appendAscii("vnd.sun.star.help://scalc/start");
852 0 : } else if (aCmdLineArgs->IsHelpDraw()) {
853 0 : bShowHelp = true;
854 0 : aHelpURLBuffer.appendAscii("vnd.sun.star.help://sdraw/start");
855 0 : } else if (aCmdLineArgs->IsHelpImpress()) {
856 0 : bShowHelp = true;
857 0 : aHelpURLBuffer.appendAscii("vnd.sun.star.help://simpress/start");
858 0 : } else if (aCmdLineArgs->IsHelpBase()) {
859 0 : bShowHelp = true;
860 0 : aHelpURLBuffer.appendAscii("vnd.sun.star.help://sdatabase/start");
861 0 : } else if (aCmdLineArgs->IsHelpBasic()) {
862 0 : bShowHelp = true;
863 0 : aHelpURLBuffer.appendAscii("vnd.sun.star.help://sbasic/start");
864 0 : } else if (aCmdLineArgs->IsHelpMath()) {
865 0 : bShowHelp = true;
866 0 : aHelpURLBuffer.appendAscii("vnd.sun.star.help://smath/start");
867 : }
868 0 : if (bShowHelp) {
869 0 : aHelpURLBuffer.appendAscii("?Language=");
870 0 : aHelpURLBuffer.append(utl::ConfigManager::getLocale());
871 : #if defined UNX
872 0 : aHelpURLBuffer.appendAscii("&System=UNX");
873 : #elif defined WNT
874 : aHelpURLBuffer.appendAscii("&System=WIN");
875 : #endif
876 : ApplicationEvent* pAppEvent = new ApplicationEvent(
877 : ApplicationEvent::TYPE_OPENHELPURL,
878 0 : aHelpURLBuffer.makeStringAndClear());
879 0 : ImplPostForeignAppEvent( pAppEvent );
880 0 : }
881 : }
882 :
883 0 : if ( bDocRequestSent )
884 : {
885 : // Send requests to dispatch watcher if we have at least one. The receiver
886 : // is responsible to delete the request after processing it.
887 0 : if ( aCmdLineArgs->HasModuleParam() )
888 : {
889 0 : SvtModuleOptions aOpt;
890 :
891 : // Support command line parameters to start a module (as preselection)
892 0 : if ( aCmdLineArgs->IsWriter() && aOpt.IsModuleInstalled( SvtModuleOptions::E_SWRITER ) )
893 0 : pRequest->aModule = aOpt.GetFactoryName( SvtModuleOptions::E_WRITER );
894 0 : else if ( aCmdLineArgs->IsCalc() && aOpt.IsModuleInstalled( SvtModuleOptions::E_SCALC ) )
895 0 : pRequest->aModule = aOpt.GetFactoryName( SvtModuleOptions::E_CALC );
896 0 : else if ( aCmdLineArgs->IsImpress() && aOpt.IsModuleInstalled( SvtModuleOptions::E_SIMPRESS ) )
897 0 : pRequest->aModule= aOpt.GetFactoryName( SvtModuleOptions::E_IMPRESS );
898 0 : else if ( aCmdLineArgs->IsDraw() && aOpt.IsModuleInstalled( SvtModuleOptions::E_SDRAW ) )
899 0 : pRequest->aModule= aOpt.GetFactoryName( SvtModuleOptions::E_DRAW );
900 : }
901 :
902 0 : ImplPostProcessDocumentsEvent( pRequest );
903 : }
904 : else
905 : {
906 : // delete not used request again
907 0 : delete pRequest;
908 0 : pRequest = NULL;
909 : }
910 0 : if (aArguments.equalsL(sc_aShowSequence, sc_nShSeqLength) ||
911 0 : aCmdLineArgs->IsEmpty())
912 : {
913 : // no document was sent, just bring Office to front
914 : ApplicationEvent* pAppEvent =
915 0 : new ApplicationEvent(ApplicationEvent::TYPE_APPEAR);
916 0 : ImplPostForeignAppEvent( pAppEvent );
917 : }
918 : }
919 :
920 : // we don't need the mutex any longer...
921 0 : aGuard.clear();
922 : // wait for processing to finish
923 0 : if (bDocRequestSent)
924 0 : cProcessed.wait();
925 : // processing finished, inform the requesting end:
926 : n = aStreamPipe.write(
927 0 : PROCESSING_DONE, SAL_N_ELEMENTS(PROCESSING_DONE));
928 : // incl. terminating NUL
929 0 : if (n != SAL_N_ELEMENTS(PROCESSING_DONE)) {
930 : SAL_WARN("desktop", "short write: " << n);
931 0 : continue;
932 0 : }
933 : }
934 : else
935 : {
936 : {
937 0 : osl::MutexGuard aGuard( GetMutex() );
938 0 : if ( mbDowning )
939 : {
940 0 : break;
941 0 : }
942 : }
943 :
944 : #if (OSL_DEBUG_LEVEL > 1) || defined DBG_UTIL
945 : fprintf( stderr, "Error on accept: %d\n", (int)nError );
946 : #endif
947 : TimeValue tval;
948 0 : tval.Seconds = 1;
949 0 : tval.Nanosec = 0;
950 0 : salhelper::Thread::wait( tval );
951 0 : }
952 0 : } while( schedule() );
953 : #endif
954 : }
955 :
956 768 : static void AddToDispatchList(
957 : DispatchWatcher::DispatchList& rDispatchList,
958 : boost::optional< OUString > const & cwdUrl,
959 : std::vector< OUString > const & aRequestList,
960 : DispatchWatcher::RequestType nType,
961 : const OUString& aParam,
962 : const OUString& aFactory )
963 : {
964 2304 : for (std::vector< OUString >::const_iterator i(aRequestList.begin());
965 1536 : i != aRequestList.end(); ++i)
966 : {
967 : rDispatchList.push_back(
968 0 : DispatchWatcher::DispatchRequest( nType, *i, cwdUrl, aParam, aFactory ));
969 : }
970 768 : }
971 :
972 96 : static void AddConversionsToDispatchList(
973 : DispatchWatcher::DispatchList& rDispatchList,
974 : boost::optional< OUString > const & cwdUrl,
975 : std::vector< OUString > const & rRequestList,
976 : const OUString& rParam,
977 : const OUString& rPrinterName,
978 : const OUString& rFactory,
979 : const OUString& rParamOut,
980 : const bool isTextCat )
981 : {
982 : DispatchWatcher::RequestType nType;
983 96 : OUString aParam( rParam );
984 :
985 96 : if( !rParam.isEmpty() )
986 : {
987 0 : nType = ( isTextCat ) ? DispatchWatcher::REQUEST_CAT : DispatchWatcher::REQUEST_CONVERSION;
988 0 : aParam = rParam;
989 : }
990 : else
991 : {
992 96 : nType = DispatchWatcher::REQUEST_BATCHPRINT;
993 96 : aParam = rPrinterName;
994 : }
995 :
996 192 : OUString aOutDir( rParamOut.trim() );
997 192 : OUString aPWD;
998 96 : ::tools::getProcessWorkingDir( aPWD );
999 :
1000 96 : if( !::osl::FileBase::getAbsoluteFileURL( aPWD, rParamOut, aOutDir ) )
1001 0 : ::osl::FileBase::getSystemPathFromFileURL( aOutDir, aOutDir );
1002 :
1003 96 : if( !rParamOut.trim().isEmpty() )
1004 : {
1005 0 : aParam += ";";
1006 0 : aParam += aOutDir;
1007 : }
1008 : else
1009 : {
1010 96 : ::osl::FileBase::getSystemPathFromFileURL( aPWD, aPWD );
1011 96 : aParam += ";" + aPWD;
1012 : }
1013 :
1014 288 : for (std::vector< OUString >::const_iterator i(rRequestList.begin());
1015 192 : i != rRequestList.end(); ++i)
1016 : {
1017 : rDispatchList.push_back(
1018 0 : DispatchWatcher::DispatchRequest( nType, *i, cwdUrl, aParam, rFactory ));
1019 96 : }
1020 96 : }
1021 :
1022 :
1023 96 : bool OfficeIPCThread::ExecuteCmdLineRequests( ProcessDocumentsRequest& aRequest )
1024 : {
1025 : // protect the dispatch list
1026 96 : osl::ClearableMutexGuard aGuard( GetMutex() );
1027 :
1028 96 : static DispatchWatcher::DispatchList aDispatchList;
1029 :
1030 192 : OUString aEmpty;
1031 : // Create dispatch list for dispatch watcher
1032 96 : AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aInFilter, DispatchWatcher::REQUEST_INFILTER, aEmpty, aRequest.aModule );
1033 96 : AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aOpenList, DispatchWatcher::REQUEST_OPEN, aEmpty, aRequest.aModule );
1034 96 : AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aViewList, DispatchWatcher::REQUEST_VIEW, aEmpty, aRequest.aModule );
1035 96 : AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aStartList, DispatchWatcher::REQUEST_START, aEmpty, aRequest.aModule );
1036 96 : AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aPrintList, DispatchWatcher::REQUEST_PRINT, aEmpty, aRequest.aModule );
1037 96 : AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aPrintToList, DispatchWatcher::REQUEST_PRINTTO, aRequest.aPrinterName, aRequest.aModule );
1038 96 : AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aForceOpenList, DispatchWatcher::REQUEST_FORCEOPEN, aEmpty, aRequest.aModule );
1039 96 : AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aForceNewList, DispatchWatcher::REQUEST_FORCENEW, aEmpty, aRequest.aModule );
1040 96 : AddConversionsToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aConversionList, aRequest.aConversionParams, aRequest.aPrinterName, aRequest.aModule, aRequest.aConversionOut, aRequest.bTextCat );
1041 96 : bool bShutdown( false );
1042 :
1043 96 : if ( pGlobalOfficeIPCThread.is() )
1044 : {
1045 96 : if( ! pGlobalOfficeIPCThread->AreRequestsEnabled() )
1046 0 : return bShutdown;
1047 :
1048 96 : pGlobalOfficeIPCThread->mnPendingRequests += aDispatchList.size();
1049 96 : if ( !pGlobalOfficeIPCThread->mpDispatchWatcher )
1050 : {
1051 96 : pGlobalOfficeIPCThread->mpDispatchWatcher = DispatchWatcher::GetDispatchWatcher();
1052 96 : pGlobalOfficeIPCThread->mpDispatchWatcher->acquire();
1053 : }
1054 :
1055 : // copy for execute
1056 96 : DispatchWatcher::DispatchList aTempList( aDispatchList );
1057 96 : aDispatchList.clear();
1058 :
1059 96 : aGuard.clear();
1060 :
1061 : // Execute dispatch requests
1062 96 : bShutdown = pGlobalOfficeIPCThread->mpDispatchWatcher->executeDispatchRequests( aTempList, s_bInEnableRequests );
1063 :
1064 : // set processed flag
1065 96 : if (aRequest.pcProcessed != NULL)
1066 0 : aRequest.pcProcessed->set();
1067 : }
1068 :
1069 192 : return bShutdown;
1070 : }
1071 :
1072 480 : }
1073 :
1074 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|