1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
10 #include <config_java.h>
16 #include <sys/types.h>
18 #include <sys/socket.h>
19 #include <arpa/inet.h>
28 #include <desktop/exithelper.h>
29 #include <osl/process.h>
30 #include <osl/thread.h>
31 #include <rtl/bootstrap.h>
32 #include <rtl/digest.h>
33 #include <rtl/process.h>
34 #include <rtl/ustrbuf.h>
41 #define PIPEDEFAULTPATH "/tmp"
42 #define PIPEALTERNATEPATH "/var/tmp"
44 /* Easier conversions: rtl_uString to rtl_String */
45 static rtl_String
*ustr_to_str(rtl_uString
*pStr
)
47 rtl_String
*pOut
= NULL
;
49 rtl_uString2String(&pOut
, rtl_uString_getStr(pStr
),
50 rtl_uString_getLength(pStr
), osl_getThreadTextEncoding(), OUSTRING_TO_OSTRING_CVTFLAGS
);
55 /* Easier conversions: char * to rtl_uString */
56 static rtl_uString
*charp_to_ustr(const char *pStr
)
58 rtl_uString
*pOut
= NULL
;
60 rtl_string2UString(&pOut
, pStr
, strlen(pStr
), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS
);
71 child_info_get_status_fd(ChildInfo
const *info
)
73 return info
->status_fd
;
77 child_info_destroy(ChildInfo
*info
)
79 close (info
->status_fd
);
80 osl_freeProcessHandle (info
->child
);
84 static ChildInfo
* child_spawn(Args
*args
, sal_Bool bAllArgs
, sal_Bool bWithStatus
)
86 rtl_uString
*pApp
= NULL
, *pTmp
= NULL
;
91 oslProcessError nError
;
93 info
= calloc (1, sizeof (ChildInfo
));
96 if (pipe(status_pipe
) < 0)
98 fprintf(stderr
, "ERROR: no file handles\n");
101 info
->status_fd
= status_pipe
[0];
103 /* application name */
104 rtl_uString_newFromAscii(&pApp
, "file://");
105 rtl_uString_newConcat(&pApp
, pApp
, args
->pAppPath
);
106 rtl_uString_newFromAscii(&pTmp
, "soffice.bin");
107 rtl_uString_newConcat(&pApp
, pApp
, pTmp
);
108 rtl_uString_release(pTmp
);
112 nArgs
= bAllArgs
? args
->nArgsTotal
: args
->nArgsEnv
;
113 ppArgs
= (rtl_uString
**)calloc(nArgs
+ 1, sizeof(rtl_uString
*));
114 for (i
= 0; i
< nArgs
; ++i
)
115 ppArgs
[i
] = args
->ppArgs
[i
];
121 /* add the pipe arg */
122 snprintf(buffer
, 63, "--splash-pipe=%d", status_pipe
[1]);
123 rtl_uString_newFromAscii( &pTmp
, buffer
);
124 ppArgs
[nArgs
] = pTmp
;
128 /* start the main process */
129 nError
= osl_executeProcess(pApp
, ppArgs
, nArgs
,
137 rtl_uString_release(pTmp
);
140 if (nError
!= osl_Process_E_None
)
142 fprintf(stderr
, "ERROR %d forking process\n", nError
);
143 rtl_uString_release(pApp
);
147 rtl_uString_release(pApp
);
148 close( status_pipe
[1] );
153 static sal_Bool
child_exited_wait(ChildInfo
*info
, sal_Bool bShortWait
)
155 TimeValue t
= { 0, 250 /* ms */ * 1000 * 1000 };
159 return osl_joinProcessWithTimeout(info
->child
, &t
) != osl_Process_E_TimedOut
;
162 static int child_get_exit_code(ChildInfo
*info
)
167 inf
.Size
= sizeof(inf
);
169 if (osl_getProcessInfo(info
->child
, osl_Process_EXITCODE
, &inf
) != osl_Process_E_None
)
171 fprintf(stderr
, "Warning: failed to fetch libreoffice exit status\n");
178 typedef enum { ProgressContinue
, ProgressRestart
, ProgressExit
} ProgressStatus
;
180 /* Path of the application, with trailing slash. */
181 static rtl_uString
*get_app_path(const char *pAppExec
)
183 char pRealPath
[PATH_MAX
];
184 rtl_uString
*pResult
;
188 char *pOrigPath
= strdup(pAppExec
);
189 char *pPath
= dirname(pOrigPath
);
191 dummy
= realpath(pPath
, pRealPath
);
193 pResult
= charp_to_ustr(pRealPath
);
196 len
= rtl_uString_getLength(pResult
);
197 if (len
> 0 && rtl_uString_getStr(pResult
)[len
- 1] != '/')
199 rtl_uString
*pSlash
= NULL
;
200 rtl_uString_newFromAscii(&pSlash
, "/");
201 rtl_uString_newConcat(&pResult
, pResult
, pSlash
);
202 rtl_uString_release(pSlash
);
208 /* Compute the OOo md5 hash from 'pText' */
209 static rtl_uString
*get_md5hash(rtl_uString
*pText
)
211 rtl_uString
*pResult
= NULL
;
212 sal_Int32 nCapacity
= 100;
213 unsigned char *pData
= NULL
;
214 sal_uInt32 nSize
= 0;
216 sal_uInt32 md5_key_len
= 0;
217 sal_uInt8
* md5_buf
= NULL
;
223 pData
= (unsigned char *)rtl_uString_getStr(pText
);
224 nSize
= rtl_uString_getLength(pText
) * sizeof(sal_Unicode
);
228 digest
= rtl_digest_create(rtl_Digest_AlgorithmMD5
);
232 md5_key_len
= rtl_digest_queryLength(digest
);
233 md5_buf
= (sal_uInt8
*)calloc(md5_key_len
, sizeof(sal_uInt8
));
235 rtl_digest_init(digest
, pData
, nSize
);
236 rtl_digest_update(digest
, pData
, nSize
);
237 rtl_digest_get(digest
, md5_buf
, md5_key_len
);
238 rtl_digest_destroy(digest
);
240 /* create hex-value string from the MD5 value to keep
241 the string size minimal */
242 rtl_uString_new_WithLength(&pResult
, nCapacity
);
243 for (; i
< md5_key_len
; ++i
)
246 snprintf(val
, 3, "%x", md5_buf
[i
]); /* sic! we ignore some of the 0's */
248 rtl_uStringbuffer_insert_ascii(&pResult
, &nCapacity
, rtl_uString_getLength(pResult
),
258 /* Construct the pipe name */
259 static rtl_uString
*get_pipe_path(rtl_uString
*pAppPath
)
261 rtl_uString
*pPath
= NULL
, *pTmp
= NULL
, *pUserInstallation
= NULL
;
262 rtl_uString
*pResult
= NULL
, *pBasePath
= NULL
, *pAbsUserInstallation
= NULL
;
263 rtlBootstrapHandle handle
;
264 rtl_uString
*pMd5hash
= NULL
;
265 sal_Unicode pUnicode
[RTL_USTR_MAX_VALUEOFINT32
];
267 /* setup bootstrap filename */
268 rtl_uString_newFromAscii(&pPath
, "file://");
269 rtl_uString_newConcat(&pPath
, pPath
, pAppPath
);
270 rtl_uString_newConcat(&pPath
, pPath
, pTmp
);
271 rtl_uString_newFromAscii(&pTmp
, SAL_CONFIGFILE("bootstrap"));
272 rtl_uString_newConcat(&pPath
, pPath
, pTmp
);
274 /* read userinstallation value */
275 handle
= rtl_bootstrap_args_open(pPath
);
277 rtl_uString_newFromAscii(&pTmp
, "UserInstallation");
278 rtl_bootstrap_get_from_handle(handle
, pTmp
, &pUserInstallation
, NULL
);
280 rtl_bootstrap_args_close(handle
);
282 /* turn it into an absolute path - unwinding symlinks etc. */
283 if (osl_getProcessWorkingDir(&pBasePath
) ||
284 osl_getAbsoluteFileURL(pBasePath
, pUserInstallation
, &pAbsUserInstallation
))
285 rtl_uString_newFromString(&pAbsUserInstallation
, pUserInstallation
);
287 /* create the pipe name */
288 pMd5hash
= get_md5hash(pAbsUserInstallation
);
290 rtl_uString_new(&pMd5hash
);
292 if (access(PIPEDEFAULTPATH
, W_OK
) == 0)
294 rtl_uString_newFromAscii(&pResult
, PIPEDEFAULTPATH
);
296 else if (access(PIPEALTERNATEPATH
, W_OK
) == 0)
298 rtl_uString_newFromAscii(&pResult
, PIPEALTERNATEPATH
);
302 fprintf(stderr
, "ERROR: no valid pipe path found.\n");
306 rtl_uString_newFromAscii(&pTmp
, "/OSL_PIPE_");
307 rtl_uString_newConcat(&pResult
, pResult
, pTmp
);
309 rtl_ustr_valueOfInt32(pUnicode
, (int)getuid(), 10);
310 rtl_uString_newFromStr(&pTmp
, pUnicode
);
311 rtl_uString_newConcat(&pResult
, pResult
, pTmp
);
313 rtl_uString_newFromAscii(&pTmp
, "_SingleOfficeIPC_");
314 rtl_uString_newConcat(&pResult
, pResult
, pTmp
);
316 rtl_uString_newConcat(&pResult
, pResult
, pMd5hash
);
319 rtl_uString_release(pMd5hash
);
320 rtl_uString_release(pPath
);
321 rtl_uString_release(pTmp
);
324 rtl_uString_release(pBasePath
);
326 rtl_uString_release(pUserInstallation
);
327 rtl_uString_release(pAbsUserInstallation
);
332 /* Get fd of the pipe of the already running OOo. */
333 static int connect_pipe(rtl_uString
*pPipePath
)
337 struct sockaddr_un addr
;
339 rtl_String
*pPipeStr
= ustr_to_str(pPipePath
);
341 memset(&addr
, 0, sizeof(addr
));
343 if ((fd
= socket(AF_UNIX
, SOCK_STREAM
, 0)) < 0)
346 (void)fcntl(fd
, F_SETFD
, FD_CLOEXEC
);
348 addr
.sun_family
= AF_UNIX
;
349 strncpy(addr
.sun_path
, rtl_string_getStr(pPipeStr
), sizeof(addr
.sun_path
) - 1);
350 rtl_string_release(pPipeStr
);
352 /* cut / paste from osl's pipe.c */
354 len
= SUN_LEN(&addr
);
359 if (connect(fd
, (struct sockaddr
*)&addr
, len
) < 0)
367 /* Escape: "," -> "\\,", "\0" -> "\\0", "\\" -> "\\\\" */
368 static rtl_uString
*escape_path(rtl_uString
const *pToEscape
)
370 rtl_uString
*pBuffer
= NULL
;
371 sal_Int32 nCapacity
= 1000;
373 sal_Int32 nEscapeLength
= rtl_uString_getLength(pToEscape
);
375 rtl_uString_new_WithLength(&pBuffer
, nCapacity
);
377 for (; i
< nEscapeLength
; ++i
)
379 sal_Unicode c
= pToEscape
->buffer
[i
];
383 rtl_uStringbuffer_insert_ascii(&pBuffer
, &nCapacity
,
384 rtl_uString_getLength(pBuffer
),
385 RTL_CONSTASCII_STRINGPARAM("\\0"));
388 rtl_uStringbuffer_insert_ascii(&pBuffer
, &nCapacity
,
389 rtl_uString_getLength(pBuffer
),
390 RTL_CONSTASCII_STRINGPARAM("\\,"));
393 rtl_uStringbuffer_insert_ascii(&pBuffer
, &nCapacity
,
394 rtl_uString_getLength(pBuffer
),
395 RTL_CONSTASCII_STRINGPARAM("\\\\"));
398 rtl_uStringbuffer_insert(&pBuffer
, &nCapacity
,
399 rtl_uString_getLength(pBuffer
),
407 /* Send args to the LO instance (using the 'fd' file descriptor) */
408 static sal_Bool
send_args(int fd
, rtl_uString
const *pCwdPath
)
410 rtl_uString
*pBuffer
= NULL
, *pTmp
= NULL
;
411 sal_Int32 nCapacity
= 1000;
412 rtl_String
*pOut
= NULL
;
415 rtl_uString
*pEscapedCwdPath
= escape_path(pCwdPath
);
417 sal_uInt32 nArgCount
= rtl_getAppCommandArgCount();
419 rtl_uString_new_WithLength(&pBuffer
, nCapacity
);
420 rtl_uString_new(&pTmp
);
422 rtl_uStringbuffer_insert_ascii(&pBuffer
, &nCapacity
,
423 rtl_uString_getLength(pBuffer
),
424 RTL_CONSTASCII_STRINGPARAM("InternalIPC::Arguments"));
426 if (rtl_uString_getLength(pEscapedCwdPath
))
428 rtl_uStringbuffer_insert_ascii(&pBuffer
, &nCapacity
,
429 rtl_uString_getLength(pBuffer
),
430 RTL_CONSTASCII_STRINGPARAM("1"));
432 rtl_uStringbuffer_insert(&pBuffer
, &nCapacity
,
433 rtl_uString_getLength(pBuffer
),
434 rtl_uString_getStr(pEscapedCwdPath
),
435 rtl_uString_getLength(pEscapedCwdPath
));
439 rtl_uStringbuffer_insert_ascii(&pBuffer
, &nCapacity
,
440 rtl_uString_getLength(pBuffer
),
441 RTL_CONSTASCII_STRINGPARAM("0"));
444 for (nArg
= 0; nArg
< nArgCount
; ++nArg
)
446 rtl_uString
*pEscapedTmp
= NULL
;
447 rtl_uStringbuffer_insert_ascii(&pBuffer
, &nCapacity
,
448 rtl_uString_getLength(pBuffer
),
451 rtl_getAppCommandArg(nArg
, &pTmp
);
453 pEscapedTmp
= escape_path(pTmp
);
455 rtl_uStringbuffer_insert(&pBuffer
, &nCapacity
,
456 rtl_uString_getLength(pBuffer
),
457 rtl_uString_getStr(pEscapedTmp
),
458 rtl_uString_getLength(pEscapedTmp
));
460 rtl_uString_release(pEscapedTmp
);
463 if (!rtl_convertUStringToString(
464 &pOut
, rtl_uString_getStr(pBuffer
),
465 rtl_uString_getLength(pBuffer
), RTL_TEXTENCODING_UTF8
,
466 (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
467 | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR
)))
469 fprintf(stderr
, "ERROR: cannot convert arguments to UTF-8\n");
473 nLen
= rtl_string_getLength(pOut
) + 1;
474 bResult
= (write(fd
, rtl_string_getStr(pOut
), nLen
) == (ssize_t
) nLen
);
478 char resp
[SAL_N_ELEMENTS("InternalIPC::ProcessingDone")];
479 ssize_t n
= read(fd
, resp
, SAL_N_ELEMENTS(resp
));
480 bResult
= n
== (ssize_t
) SAL_N_ELEMENTS(resp
)
482 resp
, "InternalIPC::ProcessingDone",
483 SAL_N_ELEMENTS(resp
))
488 rtl_uString_release(pEscapedCwdPath
);
489 rtl_uString_release(pBuffer
);
490 rtl_uString_release(pTmp
);
491 rtl_string_release(pOut
);
497 #define BUFFER_LEN 255
499 /* Read the percent to show in splash. */
500 static ProgressStatus
read_percent(ChildInfo
const *info
, int *pPercent
)
502 static char pBuffer
[BUFFER_LEN
+ 1];
503 static char *pNext
= pBuffer
;
504 static ssize_t nRead
= 0;
510 /* from the last call */
511 int nNotProcessed
= nRead
- (pNext
- pBuffer
);
512 if (nNotProcessed
>= BUFFER_LEN
)
513 return ProgressContinue
;
515 memmove(pBuffer
, pNext
, nNotProcessed
);
518 nRead
= read(child_info_get_status_fd(info
),
519 pBuffer
+ nNotProcessed
, BUFFER_LEN
- nNotProcessed
);
524 return ProgressContinue
;
529 nRead
+= nNotProcessed
;
530 pBuffer
[nRead
] = '\0';
535 for (pIter
= pBuffer
; *pIter
; ++pIter
)
544 if (!strncasecmp(pBegin
, "end", 3))
546 else if (!strncasecmp(pBegin
, "restart", 7))
547 return ProgressRestart
;
548 else if (sscanf(pBegin
, "%d%c", pPercent
, &c
) == 2 && c
== '%')
549 return ProgressContinue
;
551 /* unexpected - let's exit the splash to be safe */
555 /* Simple system check. */
556 static void system_checks(void)
561 /* check proc is mounted - lots of things fail otherwise */
562 if (stat("/proc/version", &buf
) != 0)
564 fprintf(stderr
, "ERROR: /proc not mounted - LibreOffice is unlikely to work well if at all\n");
570 static void exec_pagein (Args
*args
)
572 rtl_String
* path
= ustr_to_str(args
->pAppPath
);
573 pagein_execute(rtl_string_getStr(path
), "pagein-common");
575 if (args
->pPageinType
)
576 pagein_execute(rtl_string_getStr(path
), args
->pPageinType
);
578 rtl_string_release(path
);
581 #if HAVE_FEATURE_JAVA
583 static void extend_library_path(const char *new_element
)
585 rtl_uString
*pEnvName
=NULL
, *pOrigEnvVar
=NULL
, *pNewEnvVar
=NULL
;
586 const char *pathname
;
588 pathname
= "LIBPATH";
590 pathname
= "LD_LIBRARY_PATH";
593 rtl_uString_newFromAscii(&pEnvName
, pathname
);
594 rtl_uString_newFromAscii(&pNewEnvVar
, new_element
);
596 osl_getEnvironment(pEnvName
, &pOrigEnvVar
);
597 if (pOrigEnvVar
&& pOrigEnvVar
->length
)
599 rtl_uString
*pDelim
= NULL
;
600 rtl_uString_newFromAscii(&pDelim
, ":");
601 rtl_uString_newConcat(&pNewEnvVar
, pNewEnvVar
, pDelim
);
602 rtl_uString_newConcat(&pNewEnvVar
, pNewEnvVar
, pOrigEnvVar
);
603 rtl_uString_release(pDelim
);
606 osl_setEnvironment(pEnvName
, pNewEnvVar
);
609 rtl_uString_release(pOrigEnvVar
);
611 rtl_uString_release(pNewEnvVar
);
612 rtl_uString_release(pEnvName
);
615 static void exec_javaldx(Args
*args
)
620 rtl_uString
**ppArgs
;
621 rtl_uString
*pTmp
, *pTmp2
;
623 oslProcess javaldx
= NULL
;
624 oslFileHandle fileOut
= NULL
;
627 ppArgs
= (rtl_uString
**)calloc(args
->nArgsEnv
+ 2, sizeof(rtl_uString
*));
629 for (nArgs
= 0; nArgs
< args
->nArgsEnv
; ++nArgs
)
630 ppArgs
[nArgs
] = args
->ppArgs
[nArgs
];
632 /* Use absolute path to redirectrc */
634 rtl_uString_newFromAscii(&pTmp
, "-env:INIFILENAME=vnd.sun.star.pathname:");
635 rtl_uString_newConcat(&pTmp
, pTmp
, args
->pAppPath
);
637 rtl_uString_newFromAscii(&pTmp2
, "redirectrc");
638 rtl_uString_newConcat(&pTmp
, pTmp
, pTmp2
);
639 ppArgs
[nArgs
] = pTmp
;
640 rtl_uString_release (pTmp2
);
643 /* And also to javaldx */
645 rtl_uString_newFromAscii(&pApp
, "file://");
646 rtl_uString_newConcat(&pApp
, pApp
, args
->pAppPath
);
648 rtl_uString_newFromAscii(&pTmp
, "javaldx");
649 rtl_uString_newConcat(&pApp
, pApp
, pTmp
);
650 rtl_uString_release(pTmp
);
652 err
= osl_executeProcess_WithRedirectedIO(pApp
, ppArgs
, nArgs
,
657 &javaldx
, // process handle
662 rtl_uString_release(ppArgs
[nArgs
-1]);
663 rtl_uString_release(pApp
);
666 if(err
!= osl_Process_E_None
)
668 fprintf (stderr
, "Warning: failed to launch javaldx - java may not function correctly\n");
671 osl_freeProcessHandle(javaldx
);
673 osl_closeFile(fileOut
);
679 sal_uInt64 bytes_read
;
681 /* Magically osl_readLine doesn't work with pipes with E_SPIPE - so be this lame instead: */
682 while (osl_readFile (fileOut
, newpath
, SAL_N_ELEMENTS (newpath
), &bytes_read
) == osl_File_E_INTR
);
686 fprintf (stderr
, "Warning: failed to read path from javaldx\n");
689 osl_freeProcessHandle(javaldx
);
692 osl_closeFile(fileOut
);
697 newpath
[bytes_read
] = '\0';
699 if ((chomp
= strstr (newpath
, "\n")))
703 extend_library_path(newpath
);
706 osl_freeProcessHandle(javaldx
);
709 osl_closeFile(fileOut
);
714 // has to be a global :(
715 static oslProcess
* volatile g_pProcess
= NULL
;
717 static void sigterm_handler(int ignored
)
722 osl_terminateProcess(g_pProcess
); // forward signal to soffice.bin
728 SAL_IMPLEMENT_MAIN_WITH_ARGS(argc
, argv
)
730 sal_Bool bSentArgs
= sal_False
;
731 const char* pUsePlugin
;
732 rtl_uString
*pPipePath
= NULL
;
735 struct splash
* splash
= NULL
;
736 struct sigaction sigpipe_action
;
737 struct sigaction sigterm_action
;
739 /* turn SIGPIPE into an error */
740 memset(&sigpipe_action
, 0, sizeof(struct sigaction
));
741 sigpipe_action
.sa_handler
= SIG_IGN
;
742 sigemptyset(&sigpipe_action
.sa_mask
);
743 sigaction(SIGPIPE
, &sigpipe_action
, NULL
);
744 memset(&sigterm_action
, 0, sizeof(struct sigaction
));
745 sigterm_action
.sa_handler
= &sigterm_handler
;
746 sigemptyset(&sigterm_action
.sa_mask
);
747 sigaction(SIGTERM
, &sigterm_action
, NULL
);
750 args
->pAppPath
= get_app_path(argv
[0]);
753 fprintf(stderr
, "ERROR: Can't read app link\n");
757 #ifndef ENABLE_QUICKSTART_LIBPNG
758 /* we can't load and render it anyway */
759 args
->bInhibitSplash
= sal_True
;
762 pUsePlugin
= getenv("SAL_USE_VCLPLUGIN");
763 if (pUsePlugin
&& !strcmp(pUsePlugin
, "svp"))
764 args
->bInhibitSplash
= sal_True
;
766 if (!args
->bInhibitPipe
&& !getenv("LIBO_FLATPAK"))
769 pPipePath
= get_pipe_path(args
->pAppPath
);
771 if ((fd
=connect_pipe(pPipePath
)) >= 0)
774 char resp
[strlen("InternalIPC::SendArguments") + 1];
775 ssize_t n
= read(fd
, resp
, SAL_N_ELEMENTS(resp
));
776 if (n
== (ssize_t
) SAL_N_ELEMENTS(resp
) &&
777 (memcmp(resp
, "InternalIPC::SendArguments",
778 SAL_N_ELEMENTS(resp
) - 1) == 0))
780 rtl_uString
*pCwdPath
= NULL
;
781 osl_getProcessWorkingDir(&pCwdPath
);
784 bSentArgs
= send_args(fd
, pCwdPath
);
793 /* we have to prepare for, and exec the binary */
796 sal_Bool bAllArgs
= sal_True
;
797 sal_Bool bShortWait
, bRestart
;
799 /* sanity check pieces */
802 /* load splash image and create window */
803 if (!args
->bInhibitSplash
)
804 splash
= splash_create(args
->pAppPath
, argc
, argv
);
807 if (!args
->bInhibitPagein
)
811 #if HAVE_FEATURE_JAVA
812 if (!args
->bInhibitJavaLdx
)
818 bRestart
= sal_False
;
820 /* fast updates if we have somewhere to update it to */
821 bShortWait
= splash
? sal_True
: sal_False
;
823 /* Periodically update the splash & the percent according
824 to what status_fd says, poll quickly only while starting */
825 info
= child_spawn (args
, bAllArgs
, bShortWait
);
826 g_pProcess
= info
->child
;
828 while (!child_exited_wait(info
, bShortWait
))
830 ProgressStatus eResult
;
832 splash_draw_progress(splash
, nPercent
);
833 eResult
= read_percent(info
, &nPercent
);
835 if (eResult
!= ProgressContinue
)
837 splash_destroy(splash
);
839 bShortWait
= sal_False
;
843 status
= child_get_exit_code(info
);
844 g_pProcess
= NULL
; // reset
848 case EXITHELPER_CRASH_WITH_RESTART
: // re-start with just -env: parameters
850 bAllArgs
= sal_False
;
852 case EXITHELPER_NORMAL_RESTART
: // re-start with all arguments
860 child_info_destroy(info
);
866 rtl_uString_release(pPipePath
);
873 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */