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 namespace {
162 struct ExecutableDir : public rtl::StaticWithInit<
163  OUString, ExecutableDir> {
164  OUString operator () () {
165  OUString path;
166  if (osl_getExecutableFile( &path.pData ) != osl_Process_E_None) {
167  throw RuntimeException("cannot locate executable directory!",nullptr);
168  }
169  return path.copy( 0, path.lastIndexOf( '/' ) );
170  }
171 };
172 struct ProcessWorkingDir : public rtl::StaticWithInit<
173  OUString, ProcessWorkingDir> {
174  OUString operator () () {
175  OUString workingDir;
177  return workingDir;
178  }
179 };
180 } // anon namespace
181 
182 
183 OUString const & getExecutableDir()
184 {
185  return ExecutableDir::get();
186 }
187 
188 
189 OUString const & getProcessWorkingDir()
190 {
191  return ProcessWorkingDir::get();
192 }
193 
194 
196  OUString const & sys_path, OUString const & base_url )
197 {
198  // system path to file url
199  OUString file_url;
200  oslFileError rc = osl_getFileURLFromSystemPath( sys_path.pData, &file_url.pData );
201  if ( rc != osl_File_E_None) {
202  OUString tempPath;
203  if ( osl_getSystemPathFromFileURL( sys_path.pData, &tempPath.pData) != osl_File_E_None )
204  {
205  throw RuntimeException("cannot get file url from system path: " +
206  sys_path );
207  }
208  file_url = sys_path;
209  }
210 
211  OUString abs;
212  if (osl_getAbsoluteFileURL(
213  base_url.pData, file_url.pData, &abs.pData ) != osl_File_E_None)
214  {
215  throw RuntimeException(
216  "making absolute file url failed: \"" + base_url
217  + "\" (base-url) and \"" + file_url + "\" (file-url)!" );
218  }
219  return abs[ abs.getLength() -1 ] == '/'
220  ? abs.copy( 0, abs.getLength() -1 ) : abs;
221 }
222 
223 
224 namespace {
225 
226 
227 void printf_space( sal_Int32 space )
228 {
229  while (space--)
230  dp_misc::writeConsole(u" ");
231 }
232 
233 
234 void printf_line(
235  std::u16string_view name, std::u16string_view value, sal_Int32 level )
236 {
237  printf_space( level );
238  dp_misc::writeConsole(OUString(OUString::Concat(name) + ": " + value + "\n"));
239 }
240 
241 
242 void printf_package(
243  Reference<deployment::XPackage> const & xPackage,
244  Reference<XCommandEnvironment> const & xCmdEnv, sal_Int32 level )
245 {
246  beans::Optional< OUString > id(
247  level == 0
248  ? beans::Optional< OUString >(
249  true, dp_misc::getIdentifier( xPackage ) )
250  : xPackage->getIdentifier() );
251  if (id.IsPresent)
252  printf_line( u"Identifier", id.Value, level );
253  OUString version(xPackage->getVersion());
254  if (!version.isEmpty())
255  printf_line( u"Version", version, level + 1 );
256  printf_line( u"URL", xPackage->getURL(), level + 1 );
257 
258  beans::Optional< beans::Ambiguous<sal_Bool> > option(
259  xPackage->isRegistered( Reference<task::XAbortChannel>(), xCmdEnv ) );
260  OUString value;
261  if (option.IsPresent) {
262  beans::Ambiguous<sal_Bool> const & reg = option.Value;
263  if (reg.IsAmbiguous)
264  value = "unknown";
265  else
266  value = reg.Value ? std::u16string_view(u"yes") : std::u16string_view(u"no");
267  }
268  else
269  value = "n/a";
270  printf_line( u"is registered", value, level + 1 );
271 
272  const Reference<deployment::XPackageTypeInfo> xPackageType(
273  xPackage->getPackageType() );
274  OSL_ASSERT( xPackageType.is() );
275  if (xPackageType.is()) {
276  printf_line( u"Media-Type", xPackageType->getMediaType(), level + 1 );
277  }
278  printf_line( u"Description", xPackage->getDescription(), level + 1 );
279  if (!xPackage->isBundle())
280  return;
281 
282  Sequence< Reference<deployment::XPackage> > seq(
283  xPackage->getBundle( Reference<task::XAbortChannel>(), xCmdEnv ) );
284  printf_space( level + 1 );
285  dp_misc::writeConsole(u"bundled Packages: {\n");
286  std::vector<Reference<deployment::XPackage> >vec_bundle;
287  ::comphelper::sequenceToContainer(vec_bundle, seq);
288  printf_packages( vec_bundle, std::vector<bool>(vec_bundle.size()),
289  xCmdEnv, level + 2 );
290  printf_space( level + 1 );
291  dp_misc::writeConsole(u"}\n");
292 }
293 
294 } // anon namespace
295 
297  Reference<deployment::XPackage> const & ext)
298 {
299  OUString id(
300  dp_misc::getIdentifier(ext) );
301  printf_line( u"Identifier", id, 0 );
302  printf_space(1);
303  dp_misc::writeConsole(u"License not accepted\n\n");
304 }
305 
306 
308  std::vector< Reference<deployment::XPackage> > const & allExtensions,
309  std::vector<bool> const & vecUnaccepted,
310  Reference<XCommandEnvironment> const & xCmdEnv, sal_Int32 level )
311 {
312  OSL_ASSERT(allExtensions.size() == vecUnaccepted.size());
313 
314  if (allExtensions.empty())
315  {
316  printf_space( level );
317  dp_misc::writeConsole(u"<none>\n");
318  }
319  else
320  {
321  int index = 0;
322  for (auto const& extension : allExtensions)
323  {
324  if (vecUnaccepted[index])
325  printf_unaccepted_licenses(extension);
326  else
327  printf_package( extension, xCmdEnv, level );
329  ++index;
330  }
331  }
332 }
333 
334 
335 namespace {
336 
337 
338 Reference<XComponentContext> bootstrapStandAlone()
339 {
341  ::cppu::defaultBootstrap_InitialComponentContext();
342 
343  Reference<lang::XMultiServiceFactory> xServiceManager(
344  xContext->getServiceManager(), UNO_QUERY_THROW );
345  // set global process service factory used by unotools config helpers
346  ::comphelper::setProcessServiceFactory( xServiceManager );
347 
348  // Initialize the UCB (for backwards compatibility, in case some code still
349  // uses plain createInstance w/o args directly to obtain an instance):
350  UniversalContentBroker::create( xContext );
351 
352  return xContext;
353 }
354 
355 
356 Reference<XComponentContext> connectToOffice(
357  Reference<XComponentContext> const & xLocalComponentContext,
358  bool verbose )
359 {
360  OUString pipeId( ::dp_misc::generateRandomPipeId() );
361  OUString acceptArg = "--accept=pipe,name=" + pipeId + ";urp;";
362 
363  Sequence<OUString> args { "--nologo", "--nodefault", acceptArg };
364  OUString appURL( getExecutableDir() + "/soffice" );
365 
366  if (verbose)
367  {
368  dp_misc::writeConsole(OUString(
369  "Raising process: " + appURL +
370  "\nArguments: --nologo --nodefault " + args[2] +
371  "\n"));
372  }
373 
374  ::dp_misc::raiseProcess( appURL, args );
375 
376  if (verbose)
377  dp_misc::writeConsole(u"OK. Connecting...");
378 
379  OUString sUnoUrl = "uno:pipe,name=" + pipeId + ";urp;StarOffice.ComponentContext";
382  sUnoUrl, xLocalComponentContext ),
383  UNO_QUERY_THROW );
384  if (verbose)
385  dp_misc::writeConsole(u"OK.\n");
386 
387  return xRet;
388 }
389 
390 } // anon namespace
391 
395 static OUString getLockFilePath()
396 {
397  OUString ret;
398  OUString sBootstrap("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}");
399  rtl::Bootstrap::expandMacros(sBootstrap);
400  OUString sAbs;
401  if (::osl::File::E_None == ::osl::File::getAbsoluteFileURL(
402  sBootstrap, ".lock", sAbs))
403  {
404  if (::osl::File::E_None ==
405  ::osl::File::getSystemPathFromFileURL(sAbs, sBootstrap))
406  {
407  ret = sBootstrap;
408  }
409  }
410 
411  return ret;
412 }
413 
415  bool verbose, bool bGui, const OUString& sTempDir,
416  Reference<XComponentContext> & out_localContext)
417 {
418  // do not create any user data (for the root user) in --shared mode:
419  if (!sTempDir.isEmpty())
420  rtl::Bootstrap::set("UserInstallation", sTempDir);
421 
422  // hold lock during process runtime:
423  static ::desktop::Lockfile s_lockfile( false /* no IPC server */ );
424  Reference<XComponentContext> xComponentContext( bootstrapStandAlone() );
425  out_localContext = xComponentContext;
426  if (::dp_misc::office_is_running()) {
427  xComponentContext.set(
428  connectToOffice( xComponentContext, verbose ) );
429  }
430  else
431  {
432  if (! s_lockfile.check( nullptr ))
433  {
434  OUString sMsg(DpResId(RID_STR_CONCURRENTINSTANCE));
435  OUString sError(DpResId(RID_STR_UNOPKG_ERROR));
436 
437  sMsg += "\n" + getLockFilePath();
438 
439  if (bGui)
440  {
441  //We show a message box or print to the console that there
442  //is another instance already running
443  if ( ! InitVCL() )
444  throw RuntimeException( "Cannot initialize VCL!" );
445  {
446  std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(nullptr,
447  VclMessageType::Warning, VclButtonsType::Ok,
448  sMsg));
449  xWarn->set_title(utl::ConfigManager::getProductName());
450  xWarn->run();
451  }
452  DeInitVCL();
453  }
454 
455  throw LockFileException(sError + sMsg);
456  }
457  }
458 
459  return xComponentContext;
460 }
461 
462 }
463 
464 /* 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:412
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:333
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:431
JCOPY_OPTION option
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
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:465
float u
oslProcess raiseProcess(OUString const &appURL, Sequence< OUString > const &args)
Definition: dp_misc.cxx:376
tuple index
char const * m_name
Definition: unopkg_shared.h:35
static uno::Reference< css::uno::XComponentContext > xContext
Definition: init.cxx:2171
void TRACE(OUString const &sText)
print the text to the console in a debug build.
Definition: dp_misc.cxx:488
bool readArgument(OUString *pValue, OptionInfo const *option_info, sal_uInt32 *pIndex)
OUString const & getExecutableDir()
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)
OUString DpResId(const char *pId)
Definition: dp_shared.hxx:36
#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