LibreOffice Module desktop (master)  1
officeipcthread.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 <sal/config.h>
21 
22 #include <config_dbus.h>
23 #include <config_features.h>
24 
25 #include <app.hxx>
26 #include "officeipcthread.hxx"
27 #include "cmdlineargs.hxx"
28 #include "dispatchwatcher.hxx"
29 #include <stdio.h>
30 #include <com/sun/star/frame/TerminationVetoException.hpp>
31 #include <osl/process.h>
32 #include <sal/log.hxx>
33 #include <unotools/bootstrap.hxx>
34 #include <vcl/svapp.hxx>
35 #include <vcl/help.hxx>
36 #include <unotools/configmgr.hxx>
37 #include <osl/thread.hxx>
38 #include <rtl/digest.h>
39 #include <rtl/ustrbuf.hxx>
40 #include <rtl/instance.hxx>
41 #include <osl/conditn.hxx>
43 #include <rtl/strbuf.hxx>
45 #include <osl/file.hxx>
46 #include <rtl/process.h>
47 
48 #include <algorithm>
49 #include <cassert>
50 #include <cstdlib>
51 #include <memory>
52 
53 #if ENABLE_DBUS
54 #include <dbus/dbus.h>
55 #include <sys/socket.h>
56 #endif
57 
58 using namespace desktop;
59 using namespace ::com::sun::star::uno;
60 using namespace ::com::sun::star::lang;
61 using namespace ::com::sun::star::frame;
62 
63 namespace {
64 
65 static char const ARGUMENT_PREFIX[] = "InternalIPC::Arguments";
66 static char const SEND_ARGUMENTS[] = "InternalIPC::SendArguments";
67 static char const PROCESSING_DONE[] = "InternalIPC::ProcessingDone";
68 
69 // Receives packets from the pipe until a packet ends in a NUL character (that
70 // will not be included in the returned string) or it cannot read anything (due
71 // to error or closed pipe, in which case an empty string will be returned to
72 // signal failure):
73 OString readStringFromPipe(osl::StreamPipe const & pipe) {
74  for (OStringBuffer str;;) {
75  char buf[1024];
76  sal_Int32 n = pipe.recv(buf, SAL_N_ELEMENTS(buf));
77  if (n <= 0) {
78  SAL_INFO("desktop.app", "read empty string");
79  return "";
80  }
81  bool end = false;
82  if (buf[n - 1] == '\0') {
83  end = true;
84  --n;
85  }
86  str.append(buf, n);
87  //TODO: how does OStringBuffer.append handle overflow?
88  if (end) {
89  auto s = str.makeStringAndClear();
90  SAL_INFO("desktop.app", "read <" << s << ">");
91  return s;
92  }
93  }
94 }
95 
96 }
97 
98 namespace desktop
99 {
100 
101 namespace {
102 
103 class Parser: public CommandLineArgs::Supplier {
104 public:
105  explicit Parser(OString const & input): m_input(input) {
106  if (!m_input.match(ARGUMENT_PREFIX) ||
107  m_input.getLength() == RTL_CONSTASCII_LENGTH(ARGUMENT_PREFIX))
108  {
110  }
111  m_index = RTL_CONSTASCII_LENGTH(ARGUMENT_PREFIX);
112  switch (m_input[m_index++]) {
113  case '0':
114  break;
115  case '1':
116  {
117  OUString url;
118  if (!next(&url, false)) {
120  }
121  m_cwdUrl = url;
122  break;
123  }
124  case '2':
125  {
126  OUString path;
127  if (!next(&path, false)) {
129  }
130  OUString url;
131  if (osl::FileBase::getFileURLFromSystemPath(path, url) ==
132  osl::FileBase::E_None)
133  {
134  m_cwdUrl = url;
135  }
136  break;
137  }
138  default:
140  }
141  }
142 
143  virtual boost::optional< OUString > getCwdUrl() override { return m_cwdUrl; }
144 
145  virtual bool next(OUString * argument) override { return next(argument, true); }
146 
147 private:
148  bool next(OUString * argument, bool prefix) {
149  OSL_ASSERT(argument != nullptr);
150  if (m_index < m_input.getLength()) {
151  if (prefix) {
152  if (m_input[m_index] != ',') {
154  }
155  ++m_index;
156  }
157  OStringBuffer b;
158  while (m_index < m_input.getLength()) {
159  char c = m_input[m_index];
160  if (c == ',') {
161  break;
162  }
163  ++m_index;
164  if (c == '\\') {
165  if (m_index >= m_input.getLength())
167  c = m_input[m_index++];
168  switch (c) {
169  case '0':
170  c = '\0';
171  break;
172  case ',':
173  case '\\':
174  break;
175  default:
177  }
178  }
179  b.append(c);
180  }
181  OString b2(b.makeStringAndClear());
182  if (!rtl_convertStringToUString(
183  &argument->pData, b2.getStr(), b2.getLength(),
184  RTL_TEXTENCODING_UTF8,
185  (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR |
186  RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR |
187  RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
188  {
190  }
191  return true;
192  } else {
193  return false;
194  }
195  }
196 
198  OString m_input;
199  sal_Int32 m_index;
200 };
201 
202 bool addArgument(OStringBuffer &rArguments, char prefix,
203  const OUString &rArgument)
204 {
205  OString utf8;
206  if (!rArgument.convertToString(
207  &utf8, RTL_TEXTENCODING_UTF8,
208  (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
209  RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
210  {
211  return false;
212  }
213  rArguments.append(prefix);
214  for (sal_Int32 i = 0; i < utf8.getLength(); ++i) {
215  char c = utf8[i];
216  switch (c) {
217  case '\0':
218  rArguments.append("\\0");
219  break;
220  case ',':
221  rArguments.append("\\,");
222  break;
223  case '\\':
224  rArguments.append("\\\\");
225  break;
226  default:
227  rArguments.append(c);
228  break;
229  }
230  }
231  return true;
232 }
233 
234 }
235 
237 
238 // Turns a string in aMsg such as file:///home/foo/.libreoffice/3
239 // Into a hex string of well known length ff132a86...
240 static OUString CreateMD5FromString( const OUString& aMsg )
241 {
242  SAL_INFO("desktop.app", "create md5 from '" << aMsg << "'");
243 
244  rtlDigest handle = rtl_digest_create( rtl_Digest_AlgorithmMD5 );
245  if ( handle )
246  {
247  const sal_uInt8* pData = reinterpret_cast<const sal_uInt8*>(aMsg.getStr());
248  sal_uInt32 nSize = aMsg.getLength() * sizeof( sal_Unicode );
249  sal_uInt32 nMD5KeyLen = rtl_digest_queryLength( handle );
250  std::unique_ptr<sal_uInt8[]> pMD5KeyBuffer(new sal_uInt8[ nMD5KeyLen ]);
251 
252  rtl_digest_init( handle, pData, nSize );
253  rtl_digest_update( handle, pData, nSize );
254  rtl_digest_get( handle, pMD5KeyBuffer.get(), nMD5KeyLen );
255  rtl_digest_destroy( handle );
256 
257  // Create hex-value string from the MD5 value to keep the string size minimal
258  OUStringBuffer aBuffer( nMD5KeyLen * 2 + 1 );
259  for ( sal_uInt32 i = 0; i < nMD5KeyLen; i++ )
260  aBuffer.append( static_cast<sal_Int32>(pMD5KeyBuffer[i]), 16 );
261 
262  return aBuffer.makeStringAndClear();
263  }
264 
265  return OUString();
266 }
267 
269 {
270 public:
271  DECL_STATIC_LINK( ProcessEventsClass_Impl, CallEvent, void*, void );
272  DECL_STATIC_LINK( ProcessEventsClass_Impl, ProcessDocumentsEvent, void*, void );
273 };
274 
275 IMPL_STATIC_LINK( ProcessEventsClass_Impl, CallEvent, void*, pEvent, void )
276 {
277  // Application events are processed by the Desktop::HandleAppEvent implementation.
278  Desktop::HandleAppEvent( *static_cast<ApplicationEvent*>(pEvent) );
279  delete static_cast<ApplicationEvent*>(pEvent);
280 }
281 
282 IMPL_STATIC_LINK( ProcessEventsClass_Impl, ProcessDocumentsEvent, void*, pEvent, void )
283 {
284  // Documents requests are processed by the RequestHandler implementation
285  ProcessDocumentsRequest* pDocsRequest = static_cast<ProcessDocumentsRequest*>(pEvent);
286  RequestHandler::ExecuteCmdLineRequests(*pDocsRequest, false);
287  delete pDocsRequest;
288 }
289 
291 {
292  Application::PostUserEvent( LINK( nullptr, ProcessEventsClass_Impl, CallEvent ), pEvent );
293 }
294 
295 static void ImplPostProcessDocumentsEvent( std::unique_ptr<ProcessDocumentsRequest> pEvent )
296 {
297  Application::PostUserEvent( LINK( nullptr, ProcessEventsClass_Impl, ProcessDocumentsEvent ), pEvent.release() );
298 }
299 
300 oslSignalAction SalMainPipeExchangeSignal_impl(SAL_UNUSED_PARAMETER void* /*pData*/, oslSignalInfo* pInfo)
301 {
302  if( pInfo->Signal == osl_Signal_Terminate )
304  return osl_Signal_ActCallNextHdl;
305 }
306 
307 
308 // The RequestHandlerController implementation is a bookkeeper for all pending requests
309 // that were created by the RequestHandler. The requests are waiting to be processed by
310 // our framework loadComponentFromURL function (e.g. open/print request).
311 // During shutdown the framework is asking RequestHandlerController about pending requests.
312 // If there are pending requests framework has to stop the shutdown process. It is waiting
313 // for these requests because framework is not able to handle shutdown and open a document
314 // concurrently.
315 
316 
317 // XServiceInfo
319 {
320  return OUString( "com.sun.star.comp.RequestHandlerController" );
321 }
322 
324  OUString const & ServiceName)
325 {
326  return cppu::supportsService(this, ServiceName);
327 }
328 
330 {
331  Sequence< OUString > aSeq( 0 );
332  return aSeq;
333 }
334 
335 // XEventListener
336 void SAL_CALL RequestHandlerController::disposing( const EventObject& )
337 {
338 }
339 
340 // XTerminateListener
341 void SAL_CALL RequestHandlerController::queryTermination( const EventObject& )
342 {
343  // Desktop ask about pending request through our office ipc pipe. We have to
344  // be sure that no pending request is waiting because framework is not able to
345  // handle shutdown and open a document concurrently.
346 
348  throw TerminationVetoException();
350 }
351 
352 void SAL_CALL RequestHandlerController::notifyTermination( const EventObject& )
353 {
354 }
355 
357 public:
358  void start(RequestHandler * handler) {
359  m_handler = handler;
360  launch();
361  }
362 
363  virtual void close() = 0;
364 
365 protected:
366  explicit IpcThread(char const * name): Thread(name), m_handler(nullptr) {}
367 
368  virtual ~IpcThread() override {}
369 
370  bool process(OString const & arguments, bool * waitProcessed);
371 
373 };
374 
375 class PipeIpcThread: public IpcThread {
376 public:
378 
379 private:
380  explicit PipeIpcThread(osl::Pipe const & pipe):
381  IpcThread("PipeIPC"), pipe_(pipe)
382  {}
383 
384  virtual ~PipeIpcThread() override {}
385 
386  void execute() override;
387 
388  void close() override { pipe_.close(); }
389 
390  osl::Pipe pipe_;
391 };
392 
393 #if ENABLE_DBUS
394 
395 namespace {
396 
397 struct DbusConnectionHolder {
398  explicit DbusConnectionHolder(DBusConnection * theConnection):
399  connection(theConnection)
400  {}
401 
402  DbusConnectionHolder(DbusConnectionHolder && other): connection(nullptr)
403  { std::swap(connection, other.connection); }
404 
405  ~DbusConnectionHolder() {
406  if (connection != nullptr) {
407  dbus_connection_close(connection);
408  dbus_connection_unref(connection);
409  }
410  }
411 
412  DBusConnection * connection;
413 };
414 
415 struct DbusMessageHolder {
416  explicit DbusMessageHolder(DBusMessage * theMessage): message(theMessage) {}
417 
418  ~DbusMessageHolder() { clear(); }
419 
420  void clear() {
421  if (message != nullptr) {
422  dbus_message_unref(message);
423  }
424  message = nullptr;
425  }
426 
427  DBusMessage * message;
428 
429 private:
430  DbusMessageHolder(DbusMessageHolder const &) = delete;
431  DbusMessageHolder& operator =(DbusMessageHolder const &) = delete;
432 };
433 
434 }
435 
436 class DbusIpcThread: public IpcThread {
437 public:
439 
440 private:
441  explicit DbusIpcThread(DbusConnectionHolder && connection):
442  IpcThread("DbusIPC"), connection_(std::move(connection))
443  {}
444 
445  virtual ~DbusIpcThread() override {}
446 
447  void execute() override;
448 
449  void close() override;
450 
451  DbusConnectionHolder connection_;
452 };
453 
454 RequestHandler::Status DbusIpcThread::enable(rtl::Reference<IpcThread> * thread)
455 {
456  assert(thread != nullptr);
457  if (!dbus_threads_init_default()) {
458  SAL_WARN("desktop.app", "dbus_threads_init_default failed");
460  }
461  DBusError e;
462  dbus_error_init(&e);
463  DbusConnectionHolder con(dbus_bus_get_private(DBUS_BUS_SESSION, &e));
464  assert((con.connection == nullptr) == bool(dbus_error_is_set(&e)));
465  if (con.connection == nullptr) {
466  SAL_WARN(
467  "desktop.app",
468  "dbus_bus_get_private failed with: " << e.name << ": "
469  << e.message);
470  dbus_error_free(&e);
472  }
473  for (;;) {
474  int n = dbus_bus_request_name(
475  con.connection, "org.libreoffice.LibreOfficeIpc0",
476  DBUS_NAME_FLAG_DO_NOT_QUEUE, &e);
477  assert((n == -1) == bool(dbus_error_is_set(&e)));
478  switch (n) {
479  case -1:
480  SAL_WARN(
481  "desktop.app",
482  "dbus_bus_request_name failed with: " << e.name << ": "
483  << e.message);
484  dbus_error_free(&e);
486  case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
487  *thread = new DbusIpcThread(std::move(con));
489  case DBUS_REQUEST_NAME_REPLY_EXISTS:
490  {
491  OStringBuffer buf(ARGUMENT_PREFIX);
492  OUString arg;
494  && addArgument(buf, '1', arg)))
495  {
496  buf.append('0');
497  }
498  sal_uInt32 narg = rtl_getAppCommandArgCount();
499  for (sal_uInt32 i = 0; i != narg; ++i) {
500  rtl_getAppCommandArg(i, &arg.pData);
501  if (!addArgument(buf, ',', arg)) {
503  }
504  }
505  char const * argstr = buf.getStr();
506  DbusMessageHolder msg(
507  dbus_message_new_method_call(
508  "org.libreoffice.LibreOfficeIpc0",
509  "/org/libreoffice/LibreOfficeIpc0",
510  "org.libreoffice.LibreOfficeIpcIfc0", "Execute"));
511  if (msg.message == nullptr) {
512  SAL_WARN(
513  "desktop.app", "dbus_message_new_method_call failed");
515  }
516  DBusMessageIter it;
517  dbus_message_iter_init_append(msg.message, &it);
518  if (!dbus_message_iter_append_basic(
519  &it, DBUS_TYPE_STRING, &argstr))
520  {
521  SAL_WARN(
522  "desktop.app", "dbus_message_iter_append_basic failed");
524  }
525  DbusMessageHolder repl(
526  dbus_connection_send_with_reply_and_block(
527  con.connection, msg.message, 0x7FFFFFFF, &e));
528  assert(
529  (repl.message == nullptr) == bool(dbus_error_is_set(&e)));
530  if (repl.message == nullptr) {
531  SAL_INFO(
532  "desktop.app",
533  "dbus_connection_send_with_reply_and_block failed"
534  " with: " << e.name << ": " << e.message);
535  dbus_error_free(&e);
536  break;
537  }
539  }
540  case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
541  case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
542  SAL_WARN(
543  "desktop.app",
544  "dbus_bus_request_name failed with unexpected " << +n);
546  default:
547  for (;;) std::abort();
548  }
549  }
550 }
551 
552 void DbusIpcThread::execute()
553 {
554  assert(m_handler != nullptr);
555  m_handler->cReady.wait();
556  for (;;) {
557  {
558  osl::MutexGuard g(RequestHandler::GetMutex());
559  if (m_handler->mState == RequestHandler::State::Downing) {
560  break;
561  }
562  }
563  if (!dbus_connection_read_write(connection_.connection, -1)) {
564  break;
565  }
566  for (;;) {
567  DbusMessageHolder msg(
568  dbus_connection_pop_message(connection_.connection));
569  if (msg.message == nullptr) {
570  break;
571  }
572  if (!dbus_message_is_method_call(
573  msg.message, "org.libreoffice.LibreOfficeIpcIfc0",
574  "Execute"))
575  {
576  SAL_INFO("desktop.app", "unknown DBus message ignored");
577  continue;
578  }
579  DBusMessageIter it;
580  if (!dbus_message_iter_init(msg.message, &it)) {
581  SAL_WARN(
582  "desktop.app", "DBus message without argument ignored");
583  continue;
584  }
585  if (dbus_message_iter_get_arg_type(&it) != DBUS_TYPE_STRING) {
586  SAL_WARN(
587  "desktop.app",
588  "DBus message with non-string argument ignored");
589  continue;
590  }
591  char const * argstr;
592  dbus_message_iter_get_basic(&it, &argstr);
593  bool waitProcessed = false;
594  {
595  osl::MutexGuard g(RequestHandler::GetMutex());
596  if (!process(argstr, &waitProcessed)) {
597  continue;
598  }
599  }
600  if (waitProcessed) {
601  m_handler->cProcessed.wait();
602  }
603  DbusMessageHolder repl(dbus_message_new_method_return(msg.message));
604  if (repl.message == nullptr) {
605  SAL_WARN(
606  "desktop.app", "dbus_message_new_method_return failed");
607  continue;
608  }
609  dbus_uint32_t serial = 0;
610  if (!dbus_connection_send(
611  connection_.connection, repl.message, &serial)) {
612  SAL_WARN("desktop.app", "dbus_connection_send failed");
613  continue;
614  }
615  dbus_connection_flush(connection_.connection);
616  }
617  }
618 }
619 
620 void DbusIpcThread::close() {
621  assert(connection_.connection != nullptr);
622  // Make dbus_connection_read_write fall out of internal poll call blocking
623  // on POLLIN:
624  int fd;
625  if (!dbus_connection_get_socket(connection_.connection, &fd)) {
626  SAL_WARN("desktop.app", "dbus_connection_get_socket failed");
627  return;
628  }
629  if (shutdown(fd, SHUT_RD) == -1) {
630  auto const e = errno;
631  SAL_WARN("desktop.app", "shutdown failed with errno " << e);
632  }
633 }
634 
635 #endif
636 
637 namespace
638 {
639  class theRequestHandlerMutex
640  : public rtl::Static<osl::Mutex, theRequestHandlerMutex> {};
641 }
642 
644 {
645  return theRequestHandlerMutex::get();
646 }
647 
649 {
650  // We have the order to block all incoming requests. Framework
651  // wants to shutdown and we have to make sure that no loading/printing
652  // requests are executed anymore.
653  ::osl::MutexGuard aGuard( GetMutex() );
654 
655  if ( pGlobal.is() )
656  pGlobal->mState = State::Downing;
657 }
658 
660 {
661  // switch between just queueing the requests and executing them
662  ::osl::MutexGuard aGuard( GetMutex() );
663 
664  if ( pGlobal.is() )
665  {
666  if (pGlobal->mState != State::Downing) {
668  }
669  // hit the compiler over the head
671  // trigger already queued requests
673  }
674 }
675 
677 {
678  // Give info about pending requests
679  ::osl::MutexGuard aGuard( GetMutex() );
680  if ( pGlobal.is() )
681  return ( pGlobal->mnPendingRequests > 0 );
682  else
683  return false;
684 }
685 
687 {
688  // Remove nCount pending requests from our internal counter
689  ::osl::MutexGuard aGuard( GetMutex() );
690  if ( pGlobal.is() )
691  {
692  if ( pGlobal->mnPendingRequests > 0 )
693  pGlobal->mnPendingRequests --;
694  }
695 }
696 
698 {
699  ::osl::MutexGuard aGuard( GetMutex() );
700 
701  if( pGlobal.is() )
702  return IPC_STATUS_OK;
703 
704 #if !HAVE_FEATURE_DESKTOP || HAVE_FEATURE_MACOSX_SANDBOX
705  ipc = false;
706 #endif
707 
708  if (!ipc) {
709  pGlobal = new RequestHandler;
710  return IPC_STATUS_OK;
711  }
712 
713  enum class Kind { Pipe, Dbus };
714  Kind kind;
715 #if ENABLE_DBUS
716  kind = std::getenv("LIBO_FLATPAK") != nullptr ? Kind::Dbus : Kind::Pipe;
717 #else
718  kind = Kind::Pipe;
719 #endif
721  Status stat = Status(); // silence bogus potentially-uninitialized warnings
722  switch (kind) {
723  case Kind::Pipe:
724  stat = PipeIpcThread::enable(&thread);
725  break;
726  case Kind::Dbus:
727 #if ENABLE_DBUS
728  stat = DbusIpcThread::enable(&thread);
729  break;
730 #endif
731  default:
732  assert(false);
733  }
734  assert(thread.is() == (stat == IPC_STATUS_OK));
735  if (stat == IPC_STATUS_OK) {
736  pGlobal = new RequestHandler;
737  pGlobal->mIpcThread = thread;
738  pGlobal->mIpcThread->start(pGlobal.get());
739  }
740  return stat;
741 }
742 
744 {
745  assert(thread != nullptr);
746 
747  // The name of the named pipe is created with the hashcode of the user installation directory (without /user). We have to retrieve
748  // this information from a unotools implementation.
749  OUString aUserInstallPath;
751  if (aLocateResult != utl::Bootstrap::PATH_EXISTS
752  && aLocateResult != utl::Bootstrap::PATH_VALID)
753  {
755  }
756 
757  // Try to determine if we are the first office or not! This should prevent multiple
758  // access to the user directory !
759  // First we try to create our pipe if this fails we try to connect. We have to do this
760  // in a loop because the other office can crash or shutdown between createPipe
761  // and connectPipe!!
762  auto aUserInstallPathHashCode = CreateMD5FromString(aUserInstallPath);
763 
764  // Check result to create a hash code from the user install path
765  if ( aUserInstallPathHashCode.isEmpty() )
766  return RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR; // Something completely broken, we cannot create a valid hash code!
767 
768  osl::Pipe pipe;
769  enum PipeMode
770  {
771  PIPEMODE_DONTKNOW,
772  PIPEMODE_CREATED,
773  PIPEMODE_CONNECTED
774  };
775  PipeMode nPipeMode = PIPEMODE_DONTKNOW;
776 
777  OUString aPipeIdent( "SingleOfficeIPC_" + aUserInstallPathHashCode );
778  do
779  {
780  osl::Security security;
781 
782  // Try to create pipe
783  if ( pipe.create( aPipeIdent, osl_Pipe_CREATE, security ))
784  {
785  // Pipe created
786  nPipeMode = PIPEMODE_CREATED;
787  }
788  else if( pipe.create( aPipeIdent, osl_Pipe_OPEN, security )) // Creation not successful, now we try to connect
789  {
790  osl::StreamPipe aStreamPipe(pipe.getHandle());
791  if (readStringFromPipe(aStreamPipe) == SEND_ARGUMENTS)
792  {
793  // Pipe connected to first office
794  nPipeMode = PIPEMODE_CONNECTED;
795  }
796  else
797  {
798  // Pipe connection failed (other office exited or crashed)
799  TimeValue tval;
800  tval.Seconds = 0;
801  tval.Nanosec = 500000000;
802  salhelper::Thread::wait( tval );
803  }
804  }
805  else
806  {
807  oslPipeError eReason = pipe.getError();
808  if ((eReason == osl_Pipe_E_ConnectionRefused) || (eReason == osl_Pipe_E_invalidError))
810 
811  // Wait for second office to be ready
812  TimeValue aTimeValue;
813  aTimeValue.Seconds = 0;
814  aTimeValue.Nanosec = 10000000; // 10ms
815  salhelper::Thread::wait( aTimeValue );
816  }
817 
818  } while ( nPipeMode == PIPEMODE_DONTKNOW );
819 
820  if ( nPipeMode == PIPEMODE_CREATED )
821  {
822  // Seems we are the one and only, so create listening thread
823  *thread = new PipeIpcThread(pipe);
825  }
826  else
827  {
828  // Seems another office is running. Pipe arguments to it and self terminate
829  osl::StreamPipe aStreamPipe(pipe.getHandle());
830 
831  OStringBuffer aArguments(ARGUMENT_PREFIX);
832  OUString cwdUrl;
833  if (!(utl::Bootstrap::getProcessWorkingDir(cwdUrl) &&
834  addArgument(aArguments, '1', cwdUrl)))
835  {
836  aArguments.append('0');
837  }
838  sal_uInt32 nCount = rtl_getAppCommandArgCount();
839  for( sal_uInt32 i=0; i < nCount; i++ )
840  {
841  rtl_getAppCommandArg( i, &aUserInstallPath.pData );
842  if (!addArgument(aArguments, ',', aUserInstallPath)) {
844  }
845  }
846  aArguments.append('\0');
847  // finally, write the string onto the pipe
848  SAL_INFO("desktop.app", "writing <" << aArguments.getStr() << ">");
849  sal_Int32 n = aStreamPipe.write(
850  aArguments.getStr(), aArguments.getLength());
851  if (n != aArguments.getLength()) {
852  SAL_INFO("desktop.app", "short write: " << n);
854  }
855 
856  if (readStringFromPipe(aStreamPipe) != PROCESSING_DONE)
857  {
858  // something went wrong
860  }
861 
863  }
864 }
865 
867 {
868  osl::ClearableMutexGuard aMutex( GetMutex() );
869 
870  if( pGlobal.is() )
871  {
873  pGlobal.clear();
874 
875  handler->mState = State::Downing;
876  if (handler->mIpcThread.is()) {
877  handler->mIpcThread->close();
878  }
879 
880  // release mutex to avoid deadlocks
881  aMutex.clear();
882 
883  handler->cReady.set();
884 
885  // exit gracefully and join
886  if (handler->mIpcThread.is())
887  {
888  handler->mIpcThread->join();
889  handler->mIpcThread.clear();
890  }
891 
892  handler->cReady.reset();
893  }
894 }
895 
897  mState( State::Starting ),
898  mnPendingRequests( 0 )
899 {
900 }
901 
903 {
904  assert(!mIpcThread.is());
905 }
906 
907 void RequestHandler::SetReady(bool bIsReady)
908 {
909  osl::MutexGuard g(GetMutex());
910  if (pGlobal.is())
911  {
912  if (bIsReady)
913  pGlobal->cReady.set();
914  else
915  pGlobal->cReady.reset();
916  }
917 }
918 
920 {
922  {
923  osl::MutexGuard g(GetMutex());
924  t = pGlobal;
925  }
926  if (t.is())
927  {
928  t->cReady.wait();
929  }
930 }
931 
932 bool IpcThread::process(OString const & arguments, bool * waitProcessed) {
933  assert(waitProcessed != nullptr);
934 
935  std::unique_ptr< CommandLineArgs > aCmdLineArgs;
936  try
937  {
938  Parser p(arguments);
939  aCmdLineArgs.reset( new CommandLineArgs( p ) );
940  }
941  catch ( const CommandLineArgs::Supplier::Exception & )
942  {
943  SAL_WARN("desktop.app", "Error in received command line arguments");
944  return false;
945  }
946 
947  bool bDocRequestSent = false;
948 
949  OUString aUnknown( aCmdLineArgs->GetUnknown() );
950  if (aUnknown.isEmpty() && !aCmdLineArgs->IsHelp() && !aCmdLineArgs->IsVersion())
951  {
952  const CommandLineArgs &rCurrentCmdLineArgs = Desktop::GetCommandLineArgs();
953 
954  if ( aCmdLineArgs->IsQuickstart() )
955  {
956  // we have to use application event, because we have to start quickstart service in main thread!!
957  ApplicationEvent* pAppEvent =
959  ImplPostForeignAppEvent( pAppEvent );
960  }
961 
962  // handle request for acceptor
963  std::vector< OUString > const & accept = aCmdLineArgs->GetAccept();
964  for (auto const& elem : accept)
965  {
966  ApplicationEvent* pAppEvent = new ApplicationEvent(
968  ImplPostForeignAppEvent( pAppEvent );
969  }
970  // handle acceptor removal
971  std::vector< OUString > const & unaccept = aCmdLineArgs->GetUnaccept();
972  for (auto const& elem : unaccept)
973  {
974  ApplicationEvent* pAppEvent = new ApplicationEvent(
976  ImplPostForeignAppEvent( pAppEvent );
977  }
978 
979  std::unique_ptr<ProcessDocumentsRequest> pRequest(new ProcessDocumentsRequest(
980  aCmdLineArgs->getCwdUrl()));
981  m_handler->cProcessed.reset();
982  pRequest->pcProcessed = &m_handler->cProcessed;
983  m_handler->mbSuccess = false;
984  pRequest->mpbSuccess = &m_handler->mbSuccess;
985 
986  // Print requests are not dependent on the --invisible cmdline argument as they are
987  // loaded with the "hidden" flag! So they are always checked.
988  pRequest->aPrintList = aCmdLineArgs->GetPrintList();
989  bDocRequestSent |= !pRequest->aPrintList.empty();
990  pRequest->aPrintToList = aCmdLineArgs->GetPrintToList();
991  pRequest->aPrinterName = aCmdLineArgs->GetPrinterName();
992  bDocRequestSent |= !( pRequest->aPrintToList.empty() || pRequest->aPrinterName.isEmpty() );
993  pRequest->aConversionList = aCmdLineArgs->GetConversionList();
994  pRequest->aConversionParams = aCmdLineArgs->GetConversionParams();
995  pRequest->aConversionOut = aCmdLineArgs->GetConversionOut();
996  pRequest->aImageConversionType = aCmdLineArgs->GetImageConversionType();
997  pRequest->aInFilter = aCmdLineArgs->GetInFilter();
998  pRequest->bTextCat = aCmdLineArgs->IsTextCat();
999  pRequest->bScriptCat = aCmdLineArgs->IsScriptCat();
1000  bDocRequestSent |= !pRequest->aConversionList.empty();
1001 
1002  if ( !rCurrentCmdLineArgs.IsInvisible() )
1003  {
1004  // Read cmdline args that can open/create documents. As they would open a window
1005  // they are only allowed if the "--invisible" is currently not used!
1006  pRequest->aOpenList = aCmdLineArgs->GetOpenList();
1007  bDocRequestSent |= !pRequest->aOpenList.empty();
1008  pRequest->aViewList = aCmdLineArgs->GetViewList();
1009  bDocRequestSent |= !pRequest->aViewList.empty();
1010  pRequest->aStartList = aCmdLineArgs->GetStartList();
1011  bDocRequestSent |= !pRequest->aStartList.empty();
1012  pRequest->aForceOpenList = aCmdLineArgs->GetForceOpenList();
1013  bDocRequestSent |= !pRequest->aForceOpenList.empty();
1014  pRequest->aForceNewList = aCmdLineArgs->GetForceNewList();
1015  bDocRequestSent |= !pRequest->aForceNewList.empty();
1016 
1017  // Special command line args to create an empty document for a given module
1018 
1019  // #i18338# (lo)
1020  // we only do this if no document was specified on the command line,
1021  // since this would be inconsistent with the behaviour of
1022  // the first process, see OpenClients() (call to OpenDefault()) in app.cxx
1023  if ( aCmdLineArgs->HasModuleParam() && !bDocRequestSent )
1024  {
1025  SvtModuleOptions aOpt;
1027  if ( aCmdLineArgs->IsWriter() )
1029  else if ( aCmdLineArgs->IsCalc() )
1031  else if ( aCmdLineArgs->IsDraw() )
1033  else if ( aCmdLineArgs->IsImpress() )
1035  else if ( aCmdLineArgs->IsBase() )
1037  else if ( aCmdLineArgs->IsMath() )
1039  else if ( aCmdLineArgs->IsGlobal() )
1041  else if ( aCmdLineArgs->IsWeb() )
1043 
1044  if ( !pRequest->aOpenList.empty() )
1045  pRequest->aModule = aOpt.GetFactoryName( eFactory );
1046  else
1047  pRequest->aOpenList.push_back( aOpt.GetFactoryEmptyDocumentURL( eFactory ) );
1048  bDocRequestSent = true;
1049  }
1050  }
1051 
1052  if ( !aCmdLineArgs->IsQuickstart() ) {
1053  bool bShowHelp = false;
1054  OUStringBuffer aHelpURLBuffer;
1055  if (aCmdLineArgs->IsHelpWriter()) {
1056  bShowHelp = true;
1057  aHelpURLBuffer.append("vnd.sun.star.help://swriter/start");
1058  } else if (aCmdLineArgs->IsHelpCalc()) {
1059  bShowHelp = true;
1060  aHelpURLBuffer.append("vnd.sun.star.help://scalc/start");
1061  } else if (aCmdLineArgs->IsHelpDraw()) {
1062  bShowHelp = true;
1063  aHelpURLBuffer.append("vnd.sun.star.help://sdraw/start");
1064  } else if (aCmdLineArgs->IsHelpImpress()) {
1065  bShowHelp = true;
1066  aHelpURLBuffer.append("vnd.sun.star.help://simpress/start");
1067  } else if (aCmdLineArgs->IsHelpBase()) {
1068  bShowHelp = true;
1069  aHelpURLBuffer.append("vnd.sun.star.help://sdatabase/start");
1070  } else if (aCmdLineArgs->IsHelpBasic()) {
1071  bShowHelp = true;
1072  aHelpURLBuffer.append("vnd.sun.star.help://sbasic/start");
1073  } else if (aCmdLineArgs->IsHelpMath()) {
1074  bShowHelp = true;
1075  aHelpURLBuffer.append("vnd.sun.star.help://smath/start");
1076  }
1077  if (bShowHelp) {
1078  aHelpURLBuffer.append("?Language=");
1079  aHelpURLBuffer.append(utl::ConfigManager::getUILocale());
1080 #if defined UNX
1081  aHelpURLBuffer.append("&System=UNX");
1082 #elif defined WNT
1083  aHelpURLBuffer.append("&System=WIN");
1084 #endif
1085  ApplicationEvent* pAppEvent = new ApplicationEvent(
1087  aHelpURLBuffer.makeStringAndClear());
1088  ImplPostForeignAppEvent( pAppEvent );
1089  }
1090  }
1091 
1092  if ( bDocRequestSent )
1093  {
1094  // Send requests to dispatch watcher if we have at least one. The receiver
1095  // is responsible to delete the request after processing it.
1096  if ( aCmdLineArgs->HasModuleParam() )
1097  {
1098  SvtModuleOptions aOpt;
1099 
1100  // Support command line parameters to start a module (as preselection)
1101  if ( aCmdLineArgs->IsWriter() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
1102  pRequest->aModule = aOpt.GetFactoryName( SvtModuleOptions::EFactory::WRITER );
1103  else if ( aCmdLineArgs->IsCalc() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::CALC ) )
1104  pRequest->aModule = aOpt.GetFactoryName( SvtModuleOptions::EFactory::CALC );
1105  else if ( aCmdLineArgs->IsImpress() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::IMPRESS ) )
1106  pRequest->aModule= aOpt.GetFactoryName( SvtModuleOptions::EFactory::IMPRESS );
1107  else if ( aCmdLineArgs->IsDraw() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::DRAW ) )
1108  pRequest->aModule= aOpt.GetFactoryName( SvtModuleOptions::EFactory::DRAW );
1109  }
1110 
1111  ImplPostProcessDocumentsEvent( std::move(pRequest) );
1112  }
1113  else
1114  {
1115  // delete not used request again
1116  pRequest.reset();
1117  }
1118  if (aCmdLineArgs->IsEmpty())
1119  {
1120  // no document was sent, just bring Office to front
1121  ApplicationEvent* pAppEvent =
1123  ImplPostForeignAppEvent( pAppEvent );
1124  }
1125  }
1126  *waitProcessed = bDocRequestSent;
1127  return true;
1128 }
1129 
1131 {
1132  assert(m_handler != nullptr);
1133  do
1134  {
1135  osl::StreamPipe aStreamPipe;
1136  oslPipeError nError = pipe_.accept( aStreamPipe );
1137 
1138 
1139  if( nError == osl_Pipe_E_None )
1140  {
1141  // if we receive a request while the office is displaying some dialog or error during
1142  // bootstrap, that dialogs event loop might get events that are dispatched by this thread
1143  // we have to wait for cReady to be set by the real main loop.
1144  // only requests that don't dispatch events may be processed before cReady is set.
1145  m_handler->cReady.wait();
1146 
1147  // we might have decided to shutdown while we were sleeping
1148  if (!RequestHandler::pGlobal.is()) return;
1149 
1150  // only lock the mutex when processing starts, otherwise we deadlock when the office goes
1151  // down during wait
1152  osl::ClearableMutexGuard aGuard( RequestHandler::GetMutex() );
1153 
1155  {
1156  break;
1157  }
1158 
1159  // notify client we're ready to process its args:
1160  SAL_INFO("desktop.app", "writing <" << SEND_ARGUMENTS << ">");
1161  sal_Int32 n = aStreamPipe.write(
1162  SEND_ARGUMENTS, SAL_N_ELEMENTS(SEND_ARGUMENTS));
1163  // incl. terminating NUL
1164  if (n != SAL_N_ELEMENTS(SEND_ARGUMENTS)) {
1165  SAL_WARN("desktop.app", "short write: " << n);
1166  continue;
1167  }
1168 
1169  OString aArguments = readStringFromPipe(aStreamPipe);
1170 
1171  // Is this a lookup message from another application? if so, ignore
1172  if (aArguments.isEmpty())
1173  continue;
1174 
1175  bool waitProcessed = false;
1176  if (!process(aArguments, &waitProcessed)) {
1177  continue;
1178  }
1179 
1180  // we don't need the mutex any longer...
1181  aGuard.clear();
1182  bool bSuccess = true;
1183  // wait for processing to finish
1184  if (waitProcessed)
1185  {
1186  m_handler->cProcessed.wait();
1187  bSuccess = m_handler->mbSuccess;
1188  }
1189  if (bSuccess)
1190  {
1191  // processing finished, inform the requesting end:
1192  SAL_INFO("desktop.app", "writing <" << PROCESSING_DONE << ">");
1193  n = aStreamPipe.write(PROCESSING_DONE, SAL_N_ELEMENTS(PROCESSING_DONE));
1194  // incl. terminating NUL
1195  if (n != SAL_N_ELEMENTS(PROCESSING_DONE))
1196  {
1197  SAL_WARN("desktop.app", "short write: " << n);
1198  continue;
1199  }
1200  }
1201  }
1202  else
1203  {
1204  {
1205  osl::MutexGuard aGuard( RequestHandler::GetMutex() );
1207  {
1208  break;
1209  }
1210  }
1211 
1212  SAL_WARN( "desktop.app", "Error on accept: " << static_cast<int>(nError));
1213  TimeValue tval;
1214  tval.Seconds = 1;
1215  tval.Nanosec = 0;
1216  salhelper::Thread::wait( tval );
1217  }
1218  } while( schedule() );
1219 }
1220 
1221 static void AddToDispatchList(
1222  std::vector<DispatchWatcher::DispatchRequest>& rDispatchList,
1223  boost::optional< OUString > const & cwdUrl,
1224  std::vector< OUString > const & aRequestList,
1226  const OUString& aParam,
1227  const OUString& aFactory )
1228 {
1229  for (auto const& request : aRequestList)
1230  {
1231  rDispatchList.push_back({nType, request, cwdUrl, aParam, aFactory});
1232  }
1233 }
1234 
1236  std::vector<DispatchWatcher::DispatchRequest>& rDispatchList,
1237  boost::optional< OUString > const & cwdUrl,
1238  std::vector< OUString > const & rRequestList,
1239  const OUString& rParam,
1240  const OUString& rPrinterName,
1241  const OUString& rFactory,
1242  const OUString& rParamOut,
1243  const OUString& rImgOut,
1244  const bool isTextCat,
1245  const bool isScriptCat )
1246 {
1248  OUString aParam( rParam );
1249 
1250  if( !rParam.isEmpty() )
1251  {
1252  if ( isTextCat )
1254  else
1256  aParam = rParam;
1257  }
1258  else
1259  {
1260  if ( isScriptCat )
1262  else
1263  {
1265  aParam = rPrinterName;
1266  }
1267  }
1268 
1269  OUString aOutDir( rParamOut.trim() );
1270  OUString aImgOut( rImgOut.trim() );
1271  OUString aPWD;
1272  if (cwdUrl)
1273  {
1274  aPWD = *cwdUrl;
1275  }
1276  else
1277  {
1279  }
1280 
1281  if( !::osl::FileBase::getAbsoluteFileURL( aPWD, rParamOut, aOutDir ) )
1282  ::osl::FileBase::getSystemPathFromFileURL( aOutDir, aOutDir );
1283 
1284  if( !rParamOut.trim().isEmpty() )
1285  {
1286  aParam += ";";
1287  aParam += aOutDir;
1288  }
1289  else
1290  {
1291  ::osl::FileBase::getSystemPathFromFileURL( aPWD, aPWD );
1292  aParam += ";" + aPWD;
1293  }
1294 
1295  if( !rImgOut.trim().isEmpty() )
1296  aParam += "|" + aImgOut;
1297 
1298  for (auto const& request : rRequestList)
1299  {
1300  rDispatchList.push_back({nType, request, cwdUrl, aParam, rFactory});
1301  }
1302 }
1303 
1305 {
1306  osl::Condition* m_pCondition;
1307  ConditionSetGuard(osl::Condition* pCondition) : m_pCondition(pCondition) {}
1308  ~ConditionSetGuard() { if (m_pCondition) m_pCondition->set(); }
1309 };
1310 
1312  ProcessDocumentsRequest& aRequest, bool noTerminate)
1313 {
1314  // protect the dispatch list
1315  osl::ClearableMutexGuard aGuard( GetMutex() );
1316 
1317  // ensure that Processed flag (if exists) is signaled in any outcome
1318  ConditionSetGuard aSetGuard(aRequest.pcProcessed);
1319 
1320  static std::vector<DispatchWatcher::DispatchRequest> aDispatchList;
1321 
1322  // Create dispatch list for dispatch watcher
1323  AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aInFilter, DispatchWatcher::REQUEST_INFILTER, "", aRequest.aModule );
1324  AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aOpenList, DispatchWatcher::REQUEST_OPEN, "", aRequest.aModule );
1325  AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aViewList, DispatchWatcher::REQUEST_VIEW, "", aRequest.aModule );
1326  AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aStartList, DispatchWatcher::REQUEST_START, "", aRequest.aModule );
1327  AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aPrintList, DispatchWatcher::REQUEST_PRINT, "", aRequest.aModule );
1328  AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aPrintToList, DispatchWatcher::REQUEST_PRINTTO, aRequest.aPrinterName, aRequest.aModule );
1329  AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aForceOpenList, DispatchWatcher::REQUEST_FORCEOPEN, "", aRequest.aModule );
1330  AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aForceNewList, DispatchWatcher::REQUEST_FORCENEW, "", aRequest.aModule );
1331  AddConversionsToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aConversionList, aRequest.aConversionParams, aRequest.aPrinterName, aRequest.aModule, aRequest.aConversionOut, aRequest.aImageConversionType, aRequest.bTextCat, aRequest.bScriptCat );
1332  bool bShutdown( false );
1333 
1334  if ( pGlobal.is() )
1335  {
1336  if( ! pGlobal->AreRequestsEnabled() )
1337  {
1338  // Either starting, or downing - do not process the request, just try to bring Office to front
1339  ApplicationEvent* pAppEvent =
1341  ImplPostForeignAppEvent(pAppEvent);
1342  return bShutdown;
1343  }
1344 
1345  pGlobal->mnPendingRequests += aDispatchList.size();
1346  if ( !pGlobal->mpDispatchWatcher.is() )
1347  {
1348  pGlobal->mpDispatchWatcher = new DispatchWatcher;
1349  }
1350  rtl::Reference<DispatchWatcher> dispatchWatcher(
1351  pGlobal->mpDispatchWatcher);
1352 
1353  // copy for execute
1354  std::vector<DispatchWatcher::DispatchRequest> aTempList;
1355  aTempList.swap( aDispatchList );
1356 
1357  aGuard.clear();
1358 
1359  // Execute dispatch requests
1360  bShutdown = dispatchWatcher->executeDispatchRequests( aTempList, noTerminate);
1361  if (aRequest.mpbSuccess)
1362  *aRequest.mpbSuccess = true; // signal that we have actually succeeded
1363  }
1364 
1365  return bShutdown;
1366 }
1367 
1368 }
1369 
1370 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
rtl::Reference< IpcThread > mIpcThread
std::vector< OUString > aOpenList
PipeIpcThread(osl::Pipe const &pipe)
static PathStatus locateUserInstallation(OUString &_rURL)
static bool ExecuteCmdLineRequests(ProcessDocumentsRequest &, bool noTerminate)
static void ImplPostProcessDocumentsEvent(std::unique_ptr< ProcessDocumentsRequest > pEvent)
bool IsModuleInstalled(EModule eModule) const
static bool getProcessWorkingDir(OUString &rUrl)
std::vector< OUString > aForceNewList
ConditionSetGuard(osl::Condition *pCondition)
static ImplSVEvent * PostUserEvent(const Link< void *, void > &rLink, void *pCaller=nullptr, bool bReferenceLink=false)
Definition: app.cxx:167
std::vector< OUString > aViewList
static rtl::Reference< RequestHandler > pGlobal
virtual void SAL_CALL queryTermination(const css::lang::EventObject &aEvent) override
IMPL_STATIC_LINK(Desktop, ImplInitFilterHdl,::ConvertData &, rData, bool)
Definition: app.cxx:1753
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
sal_uInt16 sal_Unicode
virtual OUString SAL_CALL getImplementationName() override
FUNC_TYPE const nType
Thread(char const *name)
virtual sal_Bool SAL_CALL supportsService(const OUString &ServiceName) override
void start(RequestHandler *handler)
virtual void SAL_CALL notifyTermination(const css::lang::EventObject &aEvent) override
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
virtual void SAL_CALL disposing(const css::lang::EventObject &Source) override
DECL_STATIC_LINK(ProcessEventsClass_Impl, CallEvent, void *, void)
virtual ~IpcThread() override
#define SAL_N_ELEMENTS(arr)
std::vector< OUString > aForceOpenList
RequestHandler * m_handler
std::vector< OUString > aPrintToList
static bool AreRequestsPending()
sal_Int32 m_index
static void SetReady(bool bIsReady)
virtual void close()=0
int i
tuple con
unsigned char sal_Bool
static CommandLineArgs & GetCommandLineArgs()
Definition: app.cxx:386
OUString GetFactoryName(EFactory eFactory) const
OUString GetFactoryEmptyDocumentURL(EFactory eFactory) const
virtual ~RequestHandler() override
std::vector< OUString > aConversionList
static OUString getUILocale()
enumrange< T >::Iterator end(enumrange< T >)
bool IsInvisible() const
Definition: cmdlineargs.hxx:63
std::vector< OUString > aStartList
XPropertyListType t
boost::optional< OUString > m_cwdUrl
static RequestHandler::Status enable(rtl::Reference< IpcThread > *thread)
boost::optional< OUString > aCwdUrl
static void HandleAppEvent(const ApplicationEvent &rAppEvent)
Definition: app.cxx:2274
static Status Enable(bool ipc)
bool process(OString const &arguments, bool *waitProcessed)
virtual ~PipeIpcThread() override
std::vector< OUString > aPrintList
unsigned char sal_uInt8
#define SAL_INFO(area, stream)
::osl::Condition cProcessed
static void AddConversionsToDispatchList(std::vector< DispatchWatcher::DispatchRequest > &rDispatchList, boost::optional< OUString > const &cwdUrl, std::vector< OUString > const &rRequestList, const OUString &rParam, const OUString &rPrinterName, const OUString &rFactory, const OUString &rParamOut, const OUString &rImgOut, const bool isTextCat, const bool isScriptCat)
std::vector< OUString > aInFilter
oslSignalAction SalMainPipeExchangeSignal_impl(SAL_UNUSED_PARAMETER void *, oslSignalInfo *pInfo)
#define SAL_WARN(area, stream)
OString m_input
IpcThread(char const *name)
tuple next
static void AddToDispatchList(std::vector< DispatchWatcher::DispatchRequest > &rDispatchList, boost::optional< OUString > const &cwdUrl, std::vector< OUString > const &aRequestList, DispatchWatcher::RequestType nType, const OUString &aParam, const OUString &aFactory)
static OUString CreateMD5FromString(const OUString &aMsg)
static void ImplPostForeignAppEvent(ApplicationEvent *pEvent)