LibreOffice Module desktop (master)  1
unopkg_misc.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 <string_view>
23 
24 #include <config_folders.h>
25 
26 #include <vcl/svapp.hxx>
27 #include <vcl/weld.hxx>
28 #include <rtl/bootstrap.hxx>
29 #include <rtl/ustrbuf.hxx>
30 #include <sal/log.hxx>
31 #include <osl/process.h>
32 #include <osl/file.hxx>
33 #include <unotools/configmgr.hxx>
34 #include <unotools/bootstrap.hxx>
35 #include <cppuhelper/bootstrap.hxx>
36 #include <comphelper/sequence.hxx>
38 
39 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
40 #include <com/sun/star/ucb/UniversalContentBroker.hpp>
41 
42 #include <strings.hrc>
43 #include "unopkg_shared.h"
44 #include <dp_identifier.hxx>
45 #include <dp_misc.h>
46 #include <dp_shared.hxx>
47 #include <lockfile.hxx>
48 
49 using namespace ::com::sun::star;
50 using namespace ::com::sun::star::uno;
51 using namespace ::com::sun::star::ucb;
52 
53 namespace unopkg {
54 
55 OUString toString( OptionInfo const * info )
56 {
57  assert(info != nullptr);
58  OUStringBuffer buf;
59  buf.append("--");
60  buf.appendAscii(info->m_name);
61  if (info->m_short_option != '\0')
62  {
63  buf.append(" (short -" );
64  buf.append(info->m_short_option );
65  buf.append(")");
66  }
67  if (info->m_has_argument)
68  buf.append(" <argument>" );
69  return buf.makeStringAndClear();
70 }
71 
72 
74  OptionInfo const * list,
75  OUString const & opt )
76 {
77  for ( ; list->m_name != nullptr; ++list )
78  {
79  OptionInfo const & option_info = *list;
80  if (!opt.isEmpty())
81  {
82  if (opt.equalsAsciiL(
83  option_info.m_name, option_info.m_name_length ))
84  {
85  return &option_info;
86  }
87  }
88  }
89  SAL_WARN( "desktop", opt );
90  return nullptr;
91 }
92 
93 
94 bool isOption( OptionInfo const * option_info, sal_uInt32 * pIndex )
95 {
96  assert(option_info != nullptr);
97  if (osl_getCommandArgCount() <= *pIndex)
98  return false;
99 
100  OUString arg;
101  osl_getCommandArg( *pIndex, &arg.pData );
102  sal_Int32 len = arg.getLength();
103 
104  if (len < 2 || arg[ 0 ] != '-')
105  return false;
106 
107  if (len == 2 && arg[ 1 ] == option_info->m_short_option)
108  {
109  ++(*pIndex);
110  dp_misc::TRACE(__FILE__ ": identified option \'\'"
111  + OUStringChar( option_info->m_short_option ) + "\n");
112  return true;
113  }
114  if (arg[ 1 ] == '-' && rtl_ustr_ascii_compare(
115  arg.pData->buffer + 2, option_info->m_name ) == 0)
116  {
117  ++(*pIndex);
118  dp_misc::TRACE(__FILE__ ": identified option \'"
119  + OUString::createFromAscii(option_info->m_name) + "\'\n");
120  return true;
121  }
122  return false;
123 }
124 
125 
126 bool isBootstrapVariable(sal_uInt32 * pIndex)
127 {
128  OSL_ASSERT(osl_getCommandArgCount() >= *pIndex);
129 
130  OUString arg;
131  osl_getCommandArg(*pIndex, &arg.pData);
132  if (arg.match("-env:"))
133  {
134  ++(*pIndex);
135  return true;
136  }
137  return false;
138 }
139 
140 
142  OUString * pValue, OptionInfo const * option_info, sal_uInt32 * pIndex )
143 {
144  if (isOption( option_info, pIndex ))
145  {
146  if (*pIndex < osl_getCommandArgCount())
147  {
148  OSL_ASSERT( pValue != nullptr );
149  osl_getCommandArg( *pIndex, &pValue->pData );
150  dp_misc::TRACE(__FILE__ ": argument value: "
151  + *pValue + "\n");
152  ++(*pIndex);
153  return true;
154  }
155  --(*pIndex);
156  }
157  return false;
158 }
159 
160 
161 OUString const & getExecutableDir()
162 {
163  static const OUString EXEC =
164  []()
165  {
166  OUString path;
167  if (osl_getExecutableFile( &path.pData ) != osl_Process_E_None) {
168  throw RuntimeException("cannot locate executable directory!",nullptr);
169  }
170  return path.copy( 0, path.lastIndexOf( '/' ) );
171  }();
172  return EXEC;
173 }
174 
175 
176 OUString const & getProcessWorkingDir()
177 {
178  static const OUString WORKING =
179  []()
180  {
181  OUString workingDir;
183  return workingDir;
184  }();
185  return WORKING;
186 }
187 
188 
190  OUString const & sys_path, OUString const & base_url )
191 {
192  // system path to file url
193  OUString file_url;
194  oslFileError rc = osl_getFileURLFromSystemPath( sys_path.pData, &file_url.pData );
195  if ( rc != osl_File_E_None) {
196  OUString tempPath;
197  if ( osl_getSystemPathFromFileURL( sys_path.pData, &tempPath.pData) != osl_File_E_None )
198  {
199  throw RuntimeException("cannot get file url from system path: " +
200  sys_path );
201  }
202  file_url = sys_path;
203  }
204 
205  OUString abs;
206  if (osl_getAbsoluteFileURL(
207  base_url.pData, file_url.pData, &abs.pData ) != osl_File_E_None)
208  {
209  throw RuntimeException(
210  "making absolute file url failed: \"" + base_url
211  + "\" (base-url) and \"" + file_url + "\" (file-url)!" );
212  }
213  return abs[ abs.getLength() -1 ] == '/'
214  ? abs.copy( 0, abs.getLength() -1 ) : abs;
215 }
216 
217 
218 namespace {
219 
220 
221 void printf_space( sal_Int32 space )
222 {
223  while (space--)
224  dp_misc::writeConsole(u" ");
225 }
226 
227 
228 void printf_line(
229  std::u16string_view name, std::u16string_view value, sal_Int32 level )
230 {
231  printf_space( level );
232  dp_misc::writeConsole(OUString(OUString::Concat(name) + ": " + value + "\n"));
233 }
234 
235 
236 void printf_package(
237  Reference<deployment::XPackage> const & xPackage,
238  Reference<XCommandEnvironment> const & xCmdEnv, sal_Int32 level )
239 {
240  beans::Optional< OUString > id(
241  level == 0
242  ? beans::Optional< OUString >(
243  true, dp_misc::getIdentifier( xPackage ) )
244  : xPackage->getIdentifier() );
245  if (id.IsPresent)
246  printf_line( u"Identifier", id.Value, level );
247  OUString version(xPackage->getVersion());
248  if (!version.isEmpty())
249  printf_line( u"Version", version, level + 1 );
250  printf_line( u"URL", xPackage->getURL(), level + 1 );
251 
252  beans::Optional< beans::Ambiguous<sal_Bool> > option(
253  xPackage->isRegistered( Reference<task::XAbortChannel>(), xCmdEnv ) );
254  OUString value;
255  if (option.IsPresent) {
256  beans::Ambiguous<sal_Bool> const & reg = option.Value;
257  if (reg.IsAmbiguous)
258  value = "unknown";
259  else
260  value = reg.Value ? std::u16string_view(u"yes") : std::u16string_view(u"no");
261  }
262  else
263  value = "n/a";
264  printf_line( u"is registered", value, level + 1 );
265 
266  const Reference<deployment::XPackageTypeInfo> xPackageType(
267  xPackage->getPackageType() );
268  OSL_ASSERT( xPackageType.is() );
269  if (xPackageType.is()) {
270  printf_line( u"Media-Type", xPackageType->getMediaType(), level + 1 );
271  }
272  printf_line( u"Description", xPackage->getDescription(), level + 1 );
273  if (!xPackage->isBundle())
274  return;
275 
276  Sequence< Reference<deployment::XPackage> > seq(
277  xPackage->getBundle( Reference<task::XAbortChannel>(), xCmdEnv ) );
278  printf_space( level + 1 );
279  dp_misc::writeConsole(u"bundled Packages: {\n");
280  std::vector<Reference<deployment::XPackage> >vec_bundle;
281  ::comphelper::sequenceToContainer(vec_bundle, seq);
282  printf_packages( vec_bundle, std::vector<bool>(vec_bundle.size()),
283  xCmdEnv, level + 2 );
284  printf_space( level + 1 );
285  dp_misc::writeConsole(u"}\n");
286 }
287 
288 } // anon namespace
289 
291  Reference<deployment::XPackage> const & ext)
292 {
293  OUString id(
294  dp_misc::getIdentifier(ext) );
295  printf_line( u"Identifier", id, 0 );
296  printf_space(1);
297  dp_misc::writeConsole(u"License not accepted\n\n");
298 }
299 
300 
302  std::vector< Reference<deployment::XPackage> > const & allExtensions,
303  std::vector<bool> const & vecUnaccepted,
304  Reference<XCommandEnvironment> const & xCmdEnv, sal_Int32 level )
305 {
306  OSL_ASSERT(allExtensions.size() == vecUnaccepted.size());
307 
308  if (allExtensions.empty())
309  {
310  printf_space( level );
311  dp_misc::writeConsole(u"<none>\n");
312  }
313  else
314  {
315  int index = 0;
316  for (auto const& extension : allExtensions)
317  {
318  if (vecUnaccepted[index])
319  printf_unaccepted_licenses(extension);
320  else
321  printf_package( extension, xCmdEnv, level );
323  ++index;
324  }
325  }
326 }
327 
328 
329 namespace {
330 
331 
332 Reference<XComponentContext> bootstrapStandAlone()
333 {
335  ::cppu::defaultBootstrap_InitialComponentContext();
336 
337  Reference<lang::XMultiServiceFactory> xServiceManager(
338  xContext->getServiceManager(), UNO_QUERY_THROW );
339  // set global process service factory used by unotools config helpers
340  ::comphelper::setProcessServiceFactory( xServiceManager );
341 
342  // Initialize the UCB (for backwards compatibility, in case some code still
343  // uses plain createInstance w/o args directly to obtain an instance):
344  UniversalContentBroker::create( xContext );
345 
346  return xContext;
347 }
348 
349 
350 Reference<XComponentContext> connectToOffice(
351  Reference<XComponentContext> const & xLocalComponentContext,
352  bool verbose )
353 {
354  OUString pipeId( ::dp_misc::generateRandomPipeId() );
355  OUString acceptArg = "--accept=pipe,name=" + pipeId + ";urp;";
356 
357  Sequence<OUString> args { "--nologo", "--nodefault", acceptArg };
358  OUString appURL( getExecutableDir() + "/soffice" );
359 
360  if (verbose)
361  {
362  dp_misc::writeConsole(OUString(
363  "Raising process: " + appURL +
364  "\nArguments: --nologo --nodefault " + args[2] +
365  "\n"));
366  }
367 
368  ::dp_misc::raiseProcess( appURL, args );
369 
370  if (verbose)
371  dp_misc::writeConsole(u"OK. Connecting...");
372 
373  OUString sUnoUrl = "uno:pipe,name=" + pipeId + ";urp;StarOffice.ComponentContext";
376  sUnoUrl, xLocalComponentContext ),
377  UNO_QUERY_THROW );
378  if (verbose)
379  dp_misc::writeConsole(u"OK.\n");
380 
381  return xRet;
382 }
383 
384 } // anon namespace
385 
389 static OUString getLockFilePath()
390 {
391  OUString ret;
392  OUString sBootstrap("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}");
393  rtl::Bootstrap::expandMacros(sBootstrap);
394  OUString sAbs;
395  if (::osl::File::E_None == ::osl::File::getAbsoluteFileURL(
396  sBootstrap, ".lock", sAbs))
397  {
398  if (::osl::File::E_None ==
399  ::osl::File::getSystemPathFromFileURL(sAbs, sBootstrap))
400  {
401  ret = sBootstrap;
402  }
403  }
404 
405  return ret;
406 }
407 
409  bool verbose, bool bGui, const OUString& sTempDir,
410  Reference<XComponentContext> & out_localContext)
411 {
412  // do not create any user data (for the root user) in --shared mode:
413  if (!sTempDir.isEmpty())
414  rtl::Bootstrap::set("UserInstallation", sTempDir);
415 
416  // hold lock during process runtime:
417  static ::desktop::Lockfile s_lockfile( false /* no IPC server */ );
418  Reference<XComponentContext> xComponentContext( bootstrapStandAlone() );
419  out_localContext = xComponentContext;
420  if (::dp_misc::office_is_running()) {
421  xComponentContext.set(
422  connectToOffice( xComponentContext, verbose ) );
423  }
424  else
425  {
426  if (! s_lockfile.check( nullptr ))
427  {
428  OUString sMsg(DpResId(RID_STR_CONCURRENTINSTANCE));
429  OUString sError(DpResId(RID_STR_UNOPKG_ERROR));
430 
431  sMsg += "\n" + getLockFilePath();
432 
433  if (bGui)
434  {
435  //We show a message box or print to the console that there
436  //is another instance already running
437  if ( ! InitVCL() )
438  throw RuntimeException( "Cannot initialize VCL!" );
439  {
440  std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(nullptr,
441  VclMessageType::Warning, VclButtonsType::Ok,
442  sMsg));
443  xWarn->set_title(utl::ConfigManager::getProductName());
444  xWarn->run();
445  }
446  DeInitVCL();
447  }
448 
449  throw LockFileException(sError + sMsg);
450  }
451  }
452 
453  return xComponentContext;
454 }
455 
456 }
457 
458 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
OUString const & getProcessWorkingDir()
OptionInfo const * getOptionInfo(OptionInfo const *list, OUString const &opt)
Definition: unopkg_misc.cxx:73
OUString generateRandomPipeId()
Definition: dp_misc.cxx:410
void printf_packages(std::vector< Reference< deployment::XPackage > > const &allExtensions, std::vector< bool > const &vecUnaccepted, Reference< XCommandEnvironment > const &xCmdEnv, sal_Int32 level)
static bool getProcessWorkingDir(OUString &rUrl)
OUString makeAbsoluteFileUrl(OUString const &sys_path, OUString const &base_url)
bool office_is_running()
Definition: dp_misc.cxx:331
tuple args
css::uno::Reference< css::deployment::XPackageRegistry > create(css::uno::Reference< css::deployment::XPackageRegistry > const &xRootRegistry, OUString const &context, OUString const &cachePath, css::uno::Reference< css::uno::XComponentContext > const &xComponentContext)
Reference< XInterface > resolveUnoURL(OUString const &connectString, Reference< XComponentContext > const &xLocalContext, AbortChannel const *abortChannel)
Definition: dp_misc.cxx:429
JCOPY_OPTION option
sal_Unicode m_short_option
Definition: unopkg_shared.h:37
bool isOption(OptionInfo const *option_info, sal_uInt32 *pIndex)
Definition: unopkg_misc.cxx:94
VCL_DLLPUBLIC bool InitVCL()
#define SAL_CONFIGFILE(name)
Reference< XComponentContext > getUNO(bool verbose, bool bGui, const OUString &sTempDir, Reference< XComponentContext > &out_localContext)
static OUString getLockFilePath()
returns the path to the lock file used by unopkg.
static OUString getProductName()
void writeConsole(std::u16string_view sText)
writes the argument string to the console.
Definition: dp_misc.cxx:463
float u
oslProcess raiseProcess(OUString const &appURL, Sequence< OUString > const &args)
Definition: dp_misc.cxx:374
tuple index
char const * m_name
Definition: unopkg_shared.h:35
static uno::Reference< css::uno::XComponentContext > xContext
Definition: init.cxx:2261
void TRACE(OUString const &sText)
print the text to the console in a debug build.
Definition: dp_misc.cxx:486
bool readArgument(OUString *pValue, OptionInfo const *option_info, sal_uInt32 *pIndex)
OUString const & getExecutableDir()
OUString DpResId(TranslateId aId)
Definition: dp_shared.hxx:36
bool isBootstrapVariable(sal_uInt32 *pIndex)
checks if an argument is a bootstrap variable.
sal_uInt32 m_name_length
Definition: unopkg_shared.h:36
static void printf_unaccepted_licenses(Reference< deployment::XPackage > const &ext)
#define SAL_WARN(area, stream)
Degree100 abs(Degree100 x)
VCL_DLLPUBLIC void DeInitVCL()
DESKTOP_DEPLOYMENTMISC_DLLPUBLIC OUString getIdentifier(css::uno::Reference< css::deployment::XPackage > const &package)
Gets the identifier of a package.
static weld::MessageDialog * CreateMessageDialog(weld::Widget *pParent, VclMessageType eMessageType, VclButtonsType eButtonType, const OUString &rPrimaryMessage, bool bMobile=false)
OUString toString(OptionInfo const *info)
Definition: unopkg_misc.cxx:55