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 | |
10 | #include <config_features.h> |
11 | |
12 | #include <signal.h> |
13 | #include <unistd.h> |
14 | #include <limits.h> |
15 | #include <stdlib.h> |
16 | #include <sys/types.h> |
17 | #include <sys/stat.h> |
18 | #include <sys/socket.h> |
19 | #include <arpa/inet.h> |
20 | #include <sys/un.h> |
21 | #include <sys/poll.h> |
22 | #include <fcntl.h> |
23 | #include <stdio.h> |
24 | #include <libgen.h> |
25 | #include <string.h> |
26 | #include <errno.h> |
27 | |
28 | #include <osl/process.h> |
29 | #include <osl/thread.h> |
30 | #include <rtl/bootstrap.h> |
31 | #include <rtl/digest.h> |
32 | #include <rtl/process.h> |
33 | #include <rtl/ustrbuf.h> |
34 | #include <sal/main.h> |
35 | |
36 | #include "args.h" |
37 | #include "../../source/inc/exithelper.h" |
38 | #include "splashx.h" |
39 | |
40 | #define PIPEDEFAULTPATH "/tmp" |
41 | #define PIPEALTERNATEPATH "/var/tmp" |
42 | |
43 | /* Easier conversions: rtl_uString to rtl_String */ |
44 | static rtl_String * |
45 | ustr_to_str( rtl_uString *pStr ) |
46 | { |
47 | rtl_String *pOut = NULL; |
48 | |
49 | rtl_uString2String( &pOut, rtl_uString_getStr( pStr ), |
50 | rtl_uString_getLength( pStr ), osl_getThreadTextEncoding(), OUSTRING_TO_OSTRING_CVTFLAGS ); |
51 | |
52 | return pOut; |
53 | } |
54 | |
55 | /* Easier conversions: char * to rtl_uString */ |
56 | static rtl_uString * |
57 | charp_to_ustr( const char *pStr ) |
58 | { |
59 | rtl_uString *pOut = NULL; |
60 | |
61 | rtl_string2UString( &pOut, pStr, strlen( pStr ), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS ); |
62 | |
63 | return pOut; |
64 | } |
65 | |
66 | /* Easier debugging of rtl_uString values. */ |
67 | #if OSL_DEBUG_LEVEL > 1 |
68 | static void |
69 | ustr_debug( const char *pMessage, rtl_uString *pStr ) |
70 | { |
71 | rtl_String *pOut = ustr_to_str( pStr ); |
72 | |
73 | fprintf( stderr, "%s: %s\n" , pMessage, rtl_string_getStr( pOut ) ); |
74 | |
75 | rtl_string_release( pOut ); |
76 | return; |
77 | } |
78 | #else |
79 | #define ustr_debug( a, b ) {} |
80 | #endif |
81 | |
82 | typedef struct { |
83 | int status_fd; |
84 | oslProcess child; |
85 | } ChildInfo; |
86 | |
87 | static int |
88 | child_info_get_status_fd (ChildInfo *info) |
89 | { |
90 | return info->status_fd; |
91 | } |
92 | |
93 | static void |
94 | child_info_destroy (ChildInfo *info) |
95 | { |
96 | close (info->status_fd); |
97 | osl_freeProcessHandle (info->child); |
98 | free (info); |
99 | } |
100 | |
101 | static ChildInfo * |
102 | child_spawn ( Args *args, sal_Bool bAllArgs, sal_Bool bWithStatus ) |
103 | { |
104 | rtl_uString *pApp = NULL, *pTmp = NULL; |
105 | rtl_uString **ppArgs; |
106 | sal_uInt32 nArgs, i; |
107 | char buffer[64]; |
108 | ChildInfo *info; |
109 | int status_pipe[2]; |
110 | oslProcessError nError; |
111 | |
112 | info = calloc (1, sizeof (ChildInfo)); |
113 | |
114 | /* create pipe */ |
115 | if ( pipe( status_pipe ) < 0 ) |
116 | { |
117 | fprintf( stderr, "ERROR: no file handles\n" ); |
118 | exit( 1 ); |
119 | } |
120 | info->status_fd = status_pipe[0]; |
121 | |
122 | /* application name */ |
123 | rtl_uString_newFromAscii( &pApp, "file://" ); |
124 | rtl_uString_newConcat( &pApp, pApp, args->pAppPath ); |
125 | rtl_uString_newFromAscii( &pTmp, "soffice.bin" ); |
126 | rtl_uString_newConcat( &pApp, pApp, pTmp ); |
127 | rtl_uString_release( pTmp ); |
128 | pTmp = NULL; |
129 | |
130 | /* copy args */ |
131 | nArgs = bAllArgs ? args->nArgsTotal : args->nArgsEnv; |
132 | ppArgs = (rtl_uString **)calloc( nArgs + 1, sizeof( rtl_uString* ) ); |
133 | for ( i = 0; i < nArgs; ++i ) |
134 | ppArgs[i] = args->ppArgs[i]; |
135 | |
136 | if( bWithStatus ) |
137 | { |
138 | /* add the pipe arg */ |
139 | snprintf (buffer, 63, "--splash-pipe=%d" , status_pipe[1]); |
140 | rtl_uString_newFromAscii( &pTmp, buffer ); |
141 | ppArgs[nArgs] = pTmp; |
142 | ++nArgs; |
143 | } |
144 | |
145 | /* start the main process */ |
146 | nError = osl_executeProcess( pApp, ppArgs, nArgs, |
147 | osl_Process_NORMAL, |
148 | NULL, |
149 | NULL, |
150 | NULL, 0, |
151 | &info->child ); |
152 | |
153 | if (pTmp) |
154 | rtl_uString_release( pTmp ); |
155 | free (ppArgs); |
156 | |
157 | if ( nError != osl_Process_E_None ) |
158 | { |
159 | fprintf( stderr, "ERROR %d forking process" , nError ); |
160 | ustr_debug( "" , pApp ); |
161 | rtl_uString_release( pApp ); |
162 | _exit (1); |
163 | } |
164 | |
165 | rtl_uString_release( pApp ); |
166 | close( status_pipe[1] ); |
167 | |
168 | return info; |
169 | } |
170 | |
171 | static sal_Bool |
172 | child_exited_wait (ChildInfo *info, sal_Bool bShortWait) |
173 | { |
174 | TimeValue t = { 0, 250 /* ms */ * 1000 * 1000 }; |
175 | if (!bShortWait) |
176 | t.Seconds = 1024; |
177 | return osl_joinProcessWithTimeout (info->child, &t) != osl_Process_E_TimedOut; |
178 | } |
179 | |
180 | static int |
181 | child_get_exit_code (ChildInfo *info) |
182 | { |
183 | oslProcessInfo inf; |
184 | |
185 | inf.Code = -1; |
186 | inf.Size = sizeof (inf); |
187 | if (osl_getProcessInfo (info->child, osl_Process_EXITCODE, &inf) != osl_Process_E_None) |
188 | { |
189 | fprintf (stderr, "Warning: failed to fetch libreoffice exit status\n" ); |
190 | return -1; |
191 | } |
192 | return inf.Code; |
193 | } |
194 | |
195 | typedef enum { ProgressContinue, ProgressRestart, ProgressExit } ProgressStatus; |
196 | |
197 | /* Path of the application, with trailing slash. */ |
198 | static rtl_uString * |
199 | get_app_path( const char *pAppExec ) |
200 | { |
201 | char pRealPath[PATH_MAX]; |
202 | rtl_uString *pResult; |
203 | sal_Int32 len; |
204 | char* dummy; |
205 | |
206 | char *pOrigPath = strdup( pAppExec ); |
207 | char *pPath = dirname( pOrigPath ); |
208 | |
209 | dummy = realpath( pPath, pRealPath ); |
210 | (void)dummy; |
211 | pResult = charp_to_ustr( pRealPath ); |
212 | free( pOrigPath ); |
213 | |
214 | len = rtl_uString_getLength(pResult); |
215 | if (len > 0 && rtl_uString_getStr(pResult)[len - 1] != '/') |
216 | { |
217 | rtl_uString *pSlash = NULL; |
218 | rtl_uString_newFromAscii(&pSlash, "/" ); |
219 | rtl_uString_newConcat(&pResult, pResult, pSlash); |
220 | rtl_uString_release(pSlash); |
221 | } |
222 | |
223 | return pResult; |
224 | } |
225 | |
226 | /* Compute the OOo md5 hash from 'pText' */ |
227 | static rtl_uString * |
228 | get_md5hash( rtl_uString *pText ) |
229 | { |
230 | rtl_uString *pResult = NULL; |
231 | sal_Int32 nCapacity = 100; |
232 | unsigned char *pData = NULL; |
233 | sal_uInt32 nSize = 0; |
234 | rtlDigest digest; |
235 | sal_uInt32 md5_key_len = 0; |
236 | sal_uInt8* md5_buf = NULL; |
237 | sal_uInt32 i = 0; |
238 | #if OSL_DEBUG_LEVEL > 1 |
239 | rtl_String *pOut; |
240 | #endif |
241 | |
242 | if ( !pText ) |
243 | return NULL; |
244 | |
245 | #if OSL_DEBUG_LEVEL > 1 |
246 | pOut = ustr_to_str( pText ); |
247 | fprintf (stderr, "Generate pipe md5 for '%s'\n" , pOut->buffer); |
248 | rtl_string_release( pOut ); |
249 | #endif |
250 | |
251 | pData = (unsigned char *)rtl_uString_getStr( pText ); |
252 | nSize = rtl_uString_getLength( pText ) * sizeof( sal_Unicode ); |
253 | if ( !pData ) |
254 | return NULL; |
255 | |
256 | digest = rtl_digest_create( rtl_Digest_AlgorithmMD5 ); |
257 | if ( digest == 0 ) |
258 | return NULL; |
259 | |
260 | md5_key_len = rtl_digest_queryLength( digest ); |
261 | md5_buf = (sal_uInt8 *)calloc( md5_key_len, sizeof( sal_uInt8 ) ); |
262 | |
263 | rtl_digest_init( digest, pData , nSize ); |
264 | rtl_digest_update( digest, pData, nSize ); |
265 | rtl_digest_get( digest, md5_buf, md5_key_len ); |
266 | rtl_digest_destroy( digest ); |
267 | |
268 | /* create hex-value string from the MD5 value to keep |
269 | the string size minimal */ |
270 | rtl_uString_new_WithLength( &pResult, nCapacity ); |
271 | for ( ; i < md5_key_len; ++i ) |
272 | { |
273 | char val[3]; |
274 | snprintf( val, 3, "%x" , md5_buf[i] ); /* sic! we ignore some of the 0's */ |
275 | |
276 | rtl_uStringbuffer_insert_ascii( &pResult, &nCapacity, rtl_uString_getLength( pResult ), |
277 | val, strlen( val ) ); |
278 | } |
279 | |
280 | /* cleanup */ |
281 | free( md5_buf ); |
282 | |
283 | return pResult; |
284 | } |
285 | |
286 | /* Construct the pipe name */ |
287 | static rtl_uString * |
288 | get_pipe_path( rtl_uString *pAppPath ) |
289 | { |
290 | rtl_uString *pPath = NULL, *pTmp = NULL, *pUserInstallation = NULL; |
291 | rtl_uString *pResult = NULL, *pBasePath = NULL, *pAbsUserInstallation = NULL; |
292 | rtlBootstrapHandle handle; |
293 | rtl_uString *pMd5hash = NULL; |
294 | sal_Unicode pUnicode[RTL_USTR_MAX_VALUEOFINT32]; |
295 | |
296 | /* setup bootstrap filename */ |
297 | rtl_uString_newFromAscii( &pPath, "file://" ); |
298 | rtl_uString_newConcat( &pPath, pPath, pAppPath ); |
299 | rtl_uString_newConcat( &pPath, pPath, pTmp ); |
300 | rtl_uString_newFromAscii( &pTmp, SAL_CONFIGFILE( "bootstrap" ) ); |
301 | rtl_uString_newConcat( &pPath, pPath, pTmp ); |
302 | |
303 | ustr_debug( "bootstap" , pPath ); |
304 | |
305 | /* read userinstallation value */ |
306 | handle = rtl_bootstrap_args_open( pPath ); |
307 | |
308 | rtl_uString_newFromAscii( &pTmp, "UserInstallation" ); |
309 | rtl_bootstrap_get_from_handle( handle, pTmp, &pUserInstallation, NULL ); |
310 | |
311 | rtl_bootstrap_args_close( handle ); |
312 | |
313 | /* turn it into an absolute path - unwinding symlinks etc. */ |
314 | if ( osl_getProcessWorkingDir (&pBasePath) || |
315 | osl_getAbsoluteFileURL( pBasePath, pUserInstallation, &pAbsUserInstallation ) ) |
316 | rtl_uString_newFromString (&pAbsUserInstallation, pUserInstallation); |
317 | |
318 | /* create the pipe name */ |
319 | ustr_debug( "user installation" , pAbsUserInstallation ); |
320 | pMd5hash = get_md5hash( pAbsUserInstallation ); |
321 | if ( !pMd5hash ) |
322 | rtl_uString_new( &pMd5hash ); |
323 | |
324 | if ( access( PIPEDEFAULTPATH, R_OK|W_OK ) == 0 ) |
325 | rtl_uString_newFromAscii( &pResult, PIPEDEFAULTPATH ); |
326 | else |
327 | rtl_uString_newFromAscii( &pResult, PIPEALTERNATEPATH ); |
328 | |
329 | rtl_uString_newFromAscii( &pTmp, "/OSL_PIPE_" ); |
330 | rtl_uString_newConcat( &pResult, pResult, pTmp ); |
331 | |
332 | rtl_ustr_valueOfInt32( pUnicode, (int)getuid(), 10 ); |
333 | rtl_uString_newFromStr( &pTmp, pUnicode ); |
334 | rtl_uString_newConcat( &pResult, pResult, pTmp ); |
335 | |
336 | rtl_uString_newFromAscii( &pTmp, "_SingleOfficeIPC_" ); |
337 | rtl_uString_newConcat( &pResult, pResult, pTmp ); |
338 | |
339 | rtl_uString_newConcat( &pResult, pResult, pMd5hash ); |
340 | |
341 | ustr_debug( "result" , pResult ); |
342 | |
343 | /* cleanup */ |
344 | rtl_uString_release( pMd5hash ); |
345 | rtl_uString_release( pPath ); |
346 | rtl_uString_release( pTmp ); |
347 | if ( pBasePath ) |
348 | { |
349 | rtl_uString_release( pBasePath ); |
350 | } |
351 | rtl_uString_release( pUserInstallation ); |
352 | rtl_uString_release( pAbsUserInstallation ); |
353 | |
354 | return pResult; |
355 | } |
356 | |
357 | /* Get fd of the pipe of the already running OOo. */ |
358 | static int |
359 | connect_pipe( rtl_uString *pPipePath ) |
360 | { |
361 | int fd; |
362 | size_t len; |
363 | struct sockaddr_un addr; |
364 | |
365 | rtl_String *pPipeStr = ustr_to_str( pPipePath ); |
366 | |
367 | memset( &addr, 0, sizeof( addr ) ); |
368 | |
369 | if ( ( fd = socket( AF_UNIX, SOCK_STREAM, 0 ) ) < 0 ) |
370 | return fd; |
371 | |
372 | fcntl( fd, F_SETFD, FD_CLOEXEC ); |
373 | |
374 | addr.sun_family = AF_UNIX; |
375 | strncpy( addr.sun_path, rtl_string_getStr( pPipeStr ), sizeof( addr.sun_path ) - 1 ); |
376 | rtl_string_release( pPipeStr ); |
377 | |
378 | /* cut / paste from osl's pipe.c */ |
379 | #if defined(FREEBSD) |
380 | len = SUN_LEN( &addr ); |
381 | #else |
382 | len = sizeof( addr ); |
383 | #endif |
384 | |
385 | if ( connect( fd, (struct sockaddr *)&addr, len ) < 0 ) |
386 | { |
387 | close(fd); |
388 | fd = -1; |
389 | } |
390 | return fd; |
391 | } |
392 | |
393 | /* Escape: "," -> "\\,", "\0" -> "\\0", "\\" -> "\\\\" */ |
394 | static rtl_uString * |
395 | escape_path( rtl_uString *pToEscape ) |
396 | { |
397 | rtl_uString *pBuffer = NULL; |
398 | sal_Int32 nCapacity = 1000; |
399 | sal_Int32 i = 0; |
400 | sal_Int32 nEscapeLength = rtl_uString_getLength( pToEscape ); |
401 | |
402 | rtl_uString_new_WithLength( &pBuffer, nCapacity ); |
403 | |
404 | for ( ; i < nEscapeLength; ++i ) |
405 | { |
406 | sal_Unicode c = pToEscape->buffer[i]; |
407 | switch ( c ) |
408 | { |
409 | case (sal_Unicode)'\0': |
410 | rtl_uStringbuffer_insert_ascii( &pBuffer, &nCapacity, |
411 | rtl_uString_getLength( pBuffer ), |
412 | RTL_CONSTASCII_STRINGPARAM( "\\0" ) ); |
413 | break; |
414 | case (sal_Unicode)',': |
415 | rtl_uStringbuffer_insert_ascii( &pBuffer, &nCapacity, |
416 | rtl_uString_getLength( pBuffer ), |
417 | RTL_CONSTASCII_STRINGPARAM( "\\," ) ); |
418 | break; |
419 | case (sal_Unicode)'\\': |
420 | rtl_uStringbuffer_insert_ascii( &pBuffer, &nCapacity, |
421 | rtl_uString_getLength( pBuffer ), |
422 | RTL_CONSTASCII_STRINGPARAM( "\\\\" ) ); |
423 | break; |
424 | default: |
425 | rtl_uStringbuffer_insert( &pBuffer, &nCapacity, |
426 | rtl_uString_getLength( pBuffer ), |
427 | &c, 1 ); |
428 | } |
429 | } |
430 | |
431 | return pBuffer; |
432 | } |
433 | |
434 | /* Send args to the OOo instance (using the 'fd' file descriptor) */ |
435 | static sal_Bool |
436 | send_args( int fd, rtl_uString *pCwdPath ) |
437 | { |
438 | rtl_uString *pBuffer = NULL, *pTmp = NULL; |
439 | sal_Int32 nCapacity = 1000; |
440 | rtl_String *pOut = NULL; |
441 | sal_Bool bResult; |
442 | size_t nLen; |
443 | rtl_uString *pEscapedCwdPath = escape_path( pCwdPath ); |
444 | sal_uInt32 nArg = 0; |
445 | sal_uInt32 nArgCount = rtl_getAppCommandArgCount(); |
446 | |
447 | rtl_uString_new_WithLength( &pBuffer, nCapacity ); |
448 | rtl_uString_new( &pTmp ); |
449 | |
450 | rtl_uStringbuffer_insert_ascii( &pBuffer, &nCapacity, |
451 | rtl_uString_getLength( pBuffer ), |
452 | RTL_CONSTASCII_STRINGPARAM( "InternalIPC::Arguments" ) ); |
453 | |
454 | if ( rtl_uString_getLength( pEscapedCwdPath ) ) |
455 | { |
456 | rtl_uStringbuffer_insert_ascii( &pBuffer, &nCapacity, |
457 | rtl_uString_getLength( pBuffer ), |
458 | RTL_CONSTASCII_STRINGPARAM( "1" ) ); |
459 | rtl_uStringbuffer_insert( &pBuffer, &nCapacity, |
460 | rtl_uString_getLength( pBuffer ), |
461 | rtl_uString_getStr( pEscapedCwdPath ), |
462 | rtl_uString_getLength( pEscapedCwdPath ) ); |
463 | } |
464 | else |
465 | { |
466 | rtl_uStringbuffer_insert_ascii( &pBuffer, &nCapacity, |
467 | rtl_uString_getLength( pBuffer ), |
468 | RTL_CONSTASCII_STRINGPARAM( "0" ) ); |
469 | } |
470 | |
471 | for ( nArg = 0; nArg < nArgCount; ++nArg ) |
472 | { |
473 | rtl_uString *pEscapedTmp = NULL; |
474 | rtl_uStringbuffer_insert_ascii( &pBuffer, &nCapacity, |
475 | rtl_uString_getLength( pBuffer ), |
476 | "," , 1 ); |
477 | |
478 | rtl_getAppCommandArg( nArg, &pTmp ); |
479 | |
480 | pEscapedTmp = escape_path( pTmp ); |
481 | |
482 | rtl_uStringbuffer_insert( &pBuffer, &nCapacity, |
483 | rtl_uString_getLength( pBuffer ), |
484 | rtl_uString_getStr( pEscapedTmp ), |
485 | rtl_uString_getLength( pEscapedTmp ) ); |
486 | |
487 | rtl_uString_release( pEscapedTmp ); |
488 | } |
489 | |
490 | ustr_debug( "Pass args" , pBuffer ); |
491 | |
492 | if ( !rtl_convertUStringToString( |
493 | &pOut, rtl_uString_getStr( pBuffer ), |
494 | rtl_uString_getLength( pBuffer ), RTL_TEXTENCODING_UTF8, |
495 | ( RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
496 | | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR ) ) ) |
497 | { |
498 | fprintf( stderr, "ERROR: cannot convert arguments to UTF-8" ); |
499 | exit( 1 ); |
500 | } |
501 | |
502 | nLen = rtl_string_getLength( pOut ) + 1; |
503 | bResult = ( write( fd, rtl_string_getStr( pOut ), nLen ) == (ssize_t) nLen ); |
504 | |
505 | if ( bResult ) |
506 | { |
507 | char resp[ strlen( "InternalIPC::ProcessingDone" ) ]; |
508 | ssize_t n = read( fd, resp, SAL_N_ELEMENTS( resp ) ); |
509 | bResult = n == (ssize_t) SAL_N_ELEMENTS( resp ) |
510 | && (memcmp( |
511 | resp, "InternalIPC::ProcessingDone" , |
512 | SAL_N_ELEMENTS( resp ) ) |
513 | == 0); |
514 | } |
515 | |
516 | /* cleanup */ |
517 | rtl_uString_release( pEscapedCwdPath ); |
518 | rtl_uString_release( pBuffer ); |
519 | rtl_uString_release( pTmp ); |
520 | rtl_string_release( pOut ); |
521 | |
522 | return bResult; |
523 | } |
524 | |
525 | |
526 | #define BUFFER_LEN 255 |
527 | |
528 | /* Read the percent to show in splash. */ |
529 | static ProgressStatus |
530 | read_percent( ChildInfo *info, int *pPercent ) |
531 | { |
532 | static char pBuffer[BUFFER_LEN + 1]; |
533 | static char *pNext = pBuffer; |
534 | static ssize_t nRead = 0; |
535 | |
536 | char *pBegin; |
537 | char *pIter; |
538 | char c; |
539 | |
540 | /* from the last call */ |
541 | int nNotProcessed = nRead - ( pNext - pBuffer ); |
542 | if ( nNotProcessed >= BUFFER_LEN ) |
543 | return sal_False; |
544 | |
545 | memmove( pBuffer, pNext, nNotProcessed ); |
546 | |
547 | /* read data */ |
548 | nRead = read( child_info_get_status_fd (info), |
549 | pBuffer + nNotProcessed, BUFFER_LEN - nNotProcessed ); |
550 | if ( nRead < 0 ) { |
551 | if (errno == EINTR) |
552 | return ProgressContinue; |
553 | return ProgressExit; |
554 | } |
555 | |
556 | nRead += nNotProcessed; |
557 | pBuffer[nRead] = '\0'; |
558 | |
559 | /* skip old data */ |
560 | pBegin = pBuffer; |
561 | pNext = pBuffer; |
562 | for ( pIter = pBuffer; *pIter; ++pIter ) |
563 | { |
564 | if ( *pIter == '\n' ) |
565 | { |
566 | pBegin = pNext; |
567 | pNext = pIter + 1; |
568 | } |
569 | } |
570 | |
571 | #if OSL_DEBUG_LEVEL > 1 |
572 | fprintf( stderr, "Got status: %s\n" , pBegin ); |
573 | #endif |
574 | if ( !strncasecmp( pBegin, "end" , 3 ) ) |
575 | return ProgressExit; |
576 | else if ( !strncasecmp( pBegin, "restart" , 7 ) ) |
577 | return ProgressRestart; |
578 | else if ( sscanf( pBegin, "%d%c" , pPercent, &c ) == 2 && c == '%' ) |
579 | return ProgressContinue; |
580 | |
581 | /* unexpected - let's exit the splash to be safe */ |
582 | return ProgressExit; |
583 | } |
584 | |
585 | /* Simple system check. */ |
586 | static void |
587 | system_checks( void ) |
588 | { |
589 | #ifdef LINUX |
590 | struct stat buf; |
591 | |
592 | /* check proc is mounted - lots of things fail otherwise */ |
593 | if ( stat( "/proc/version" , &buf ) != 0 ) |
594 | { |
595 | fprintf( stderr, "ERROR: /proc not mounted - LibreOffice is unlikely to work well if at all" ); |
596 | exit( 1 ); |
597 | } |
598 | #endif |
599 | } |
600 | |
601 | /* re-use the pagein code */ |
602 | extern int pagein_execute (int argc, char **argv); |
603 | |
604 | static char *build_pagein_path (Args *args, const char *pagein_name) |
605 | { |
606 | char *path; |
607 | rtl_String *app_path; |
608 | |
609 | app_path = ustr_to_str (args->pAppPath); |
610 | path = malloc ( |
611 | RTL_CONSTASCII_LENGTH("@" ) + app_path->length + strlen (pagein_name) + |
612 | 1); |
613 | strcpy (path, "@" ); |
614 | strcpy (path + 1, rtl_string_getStr (app_path)); |
615 | strcat (path, pagein_name); |
616 | |
617 | rtl_string_release( app_path ); |
618 | |
619 | return path; |
620 | } |
621 | |
622 | void |
623 | exec_pagein (Args *args) |
624 | { |
625 | char *argv[3]; |
626 | |
627 | /* don't use -L - since that does a chdir that breaks relative paths */ |
628 | argv[0] = "dummy-pagein" ; |
629 | argv[1] = build_pagein_path (args, "pagein-common" ); |
630 | if (args->pPageinType) { |
631 | argv[2] = build_pagein_path (args, args->pPageinType); |
632 | } else |
633 | argv[2] = NULL; |
634 | |
635 | pagein_execute (args->pPageinType ? 3 : 2, argv); |
636 | |
637 | if (argv[2]) |
638 | free (argv[2]); |
639 | free (argv[1]); |
640 | } |
641 | |
642 | #if HAVE_FEATURE_JAVA |
643 | |
644 | static void extend_library_path (const char *new_element) |
645 | { |
646 | rtl_uString *pEnvName=NULL, *pOrigEnvVar=NULL, *pNewEnvVar=NULL; |
647 | const char *pathname; |
648 | #ifdef AIX |
649 | pathname = "LIBPATH" ; |
650 | #else |
651 | pathname = "LD_LIBRARY_PATH" ; |
652 | #endif |
653 | |
654 | rtl_uString_newFromAscii( &pEnvName, pathname ); |
655 | rtl_uString_newFromAscii( &pNewEnvVar, new_element ); |
656 | |
657 | osl_getEnvironment( pEnvName, &pOrigEnvVar ); |
658 | if (pOrigEnvVar && pOrigEnvVar->length) |
659 | { |
660 | rtl_uString *pDelim = NULL; |
661 | rtl_uString_newFromAscii( &pDelim, ":" ); |
662 | rtl_uString_newConcat( &pNewEnvVar, pNewEnvVar, pDelim ); |
663 | rtl_uString_newConcat( &pNewEnvVar, pNewEnvVar, pOrigEnvVar ); |
664 | rtl_uString_release( pDelim ); |
665 | } |
666 | |
667 | osl_setEnvironment( pEnvName, pNewEnvVar ); |
668 | |
669 | if (pOrigEnvVar) |
670 | rtl_uString_release( pOrigEnvVar ); |
671 | rtl_uString_release( pNewEnvVar ); |
672 | rtl_uString_release( pEnvName ); |
673 | } |
674 | |
675 | static void |
676 | exec_javaldx (Args *args) |
677 | { |
678 | char newpath[4096]; |
679 | sal_uInt32 nArgs; |
680 | rtl_uString *pApp; |
681 | rtl_uString **ppArgs; |
682 | rtl_uString *pTmp, *pTmp2; |
683 | |
684 | oslProcess javaldx = NULL; |
685 | oslFileHandle fileOut= 0; |
686 | oslProcessError err; |
687 | |
688 | ppArgs = (rtl_uString **)calloc( args->nArgsEnv + 2, sizeof( rtl_uString* ) ); |
689 | |
690 | for ( nArgs = 0; nArgs < args->nArgsEnv; ++nArgs ) |
691 | ppArgs[nArgs] = args->ppArgs[nArgs]; |
692 | |
693 | /* Use absolute path to redirectrc */ |
694 | pTmp = NULL; |
695 | rtl_uString_newFromAscii( &pTmp, "-env:INIFILENAME=vnd.sun.star.pathname:" ); |
696 | rtl_uString_newConcat( &pTmp, pTmp, args->pAppPath ); |
697 | pTmp2 = NULL; |
698 | rtl_uString_newFromAscii( &pTmp2, "redirectrc" ); |
699 | rtl_uString_newConcat( &pTmp, pTmp, pTmp2 ); |
700 | ppArgs[nArgs] = pTmp; |
701 | rtl_uString_release (pTmp2); |
702 | nArgs++; |
703 | |
704 | /* And also to javaldx */ |
705 | pApp = NULL; |
706 | rtl_uString_newFromAscii( &pApp, "file://" ); |
707 | rtl_uString_newConcat( &pApp, pApp, args->pAppPath ); |
708 | pTmp = NULL; |
709 | rtl_uString_newFromAscii( &pTmp, "../ure-link/bin/javaldx" ); |
710 | rtl_uString_newConcat( &pApp, pApp, pTmp ); |
711 | rtl_uString_release( pTmp ); |
712 | |
713 | err = osl_executeProcess_WithRedirectedIO( pApp, ppArgs, nArgs, |
714 | osl_Process_NORMAL, |
715 | NULL, // security |
716 | NULL, // work dir |
717 | NULL, 0, |
718 | &javaldx, // process handle |
719 | NULL, |
720 | &fileOut, |
721 | NULL); |
722 | |
723 | rtl_uString_release( ppArgs[nArgs-1] ); |
724 | rtl_uString_release( pApp ); |
725 | free( ppArgs ); |
726 | |
727 | if( err != osl_Process_E_None) |
728 | { |
729 | fprintf (stderr, "Warning: failed to launch javaldx - java may not function correctly\n" ); |
730 | if (javaldx) |
731 | osl_freeProcessHandle(javaldx); |
732 | if (fileOut) |
733 | osl_closeFile(fileOut); |
734 | return; |
735 | } else { |
736 | char *chomp; |
737 | sal_uInt64 bytes_read; |
738 | |
739 | /* Magically osl_readLine doesn't work with pipes with E_SPIPE - so be this lame instead: */ |
740 | while (osl_readFile (fileOut, newpath, SAL_N_ELEMENTS (newpath), &bytes_read) == osl_File_E_INTR); |
741 | |
742 | if (bytes_read <= 0) { |
743 | fprintf (stderr, "Warning: failed to read path from javaldx\n" ); |
744 | if (javaldx) |
745 | osl_freeProcessHandle(javaldx); |
746 | if (fileOut) |
747 | osl_closeFile(fileOut); |
748 | return; |
749 | } |
750 | newpath[bytes_read] = '\0'; |
751 | |
752 | if ((chomp = strstr (newpath, "\n" ))) |
753 | *chomp = '\0'; |
754 | } |
755 | |
756 | #if OSL_DEBUG_LEVEL > 1 |
757 | fprintf (stderr, "Adding javaldx path of '%s'\n" , newpath); |
758 | #endif |
759 | extend_library_path (newpath); |
760 | |
761 | if (javaldx) |
762 | osl_freeProcessHandle(javaldx); |
763 | if (fileOut) |
764 | osl_closeFile(fileOut); |
765 | } |
766 | |
767 | #endif |
768 | |
769 | // has to be a global :( |
770 | oslProcess * volatile g_pProcess = 0; |
771 | |
772 | void sigterm_handler(int ignored) |
773 | { |
774 | (void) ignored; |
775 | if (g_pProcess) |
776 | { |
777 | // forward signal to soffice.bin |
778 | osl_terminateProcess(g_pProcess); |
779 | } |
780 | _exit(255); |
781 | } |
782 | |
783 | |
784 | SAL_IMPLEMENT_MAIN_WITH_ARGS( argc, argv ) |
785 | { |
786 | int fd = 0; |
787 | sal_Bool bSentArgs = sal_False; |
788 | const char* pUsePlugin; |
789 | rtl_uString *pPipePath = NULL; |
790 | Args *args; |
791 | int status = 0; |
792 | struct splash* splash = NULL; |
793 | struct sigaction sigpipe_action; |
794 | struct sigaction sigterm_action; |
795 | |
796 | /* turn SIGPIPE into an error */ |
797 | memset(&sigpipe_action, 0, sizeof(struct sigaction)); |
798 | sigpipe_action.sa_handler = SIG_IGN; |
799 | sigemptyset(&sigpipe_action.sa_mask); |
800 | sigaction(SIGPIPE, &sigpipe_action, 0); |
801 | memset(&sigterm_action, 0, sizeof(struct sigaction)); |
802 | sigterm_action.sa_handler = &sigterm_handler; |
803 | sigemptyset(&sigterm_action.sa_mask); |
804 | sigaction(SIGTERM, &sigterm_action, 0); |
805 | |
806 | args = args_parse (); |
807 | args->pAppPath = get_app_path( argv[0] ); |
808 | if ( !args->pAppPath ) |
809 | { |
810 | fprintf( stderr, "ERROR: Can't read app link\n" ); |
811 | exit( 1 ); |
812 | } |
813 | ustr_debug( "App path" , args->pAppPath ); |
814 | |
815 | #ifndef ENABLE_QUICKSTART_LIBPNG |
816 | /* we can't load and render it anyway */ |
817 | args->bInhibitSplash = sal_True; |
818 | #endif |
819 | |
820 | pUsePlugin = getenv( "SAL_USE_VCLPLUGIN" ); |
821 | if ( pUsePlugin && !strcmp(pUsePlugin, "svp" ) ) |
822 | args->bInhibitSplash = sal_True; |
823 | |
824 | if ( !args->bInhibitPipe ) |
825 | { |
826 | pPipePath = get_pipe_path( args->pAppPath ); |
827 | |
828 | if ( ( fd = connect_pipe( pPipePath ) ) >= 0 ) |
829 | { |
830 | // Wait for answer |
831 | char resp[ strlen( "InternalIPC::SendArguments" ) + 1]; |
832 | ssize_t n = read( fd, resp, SAL_N_ELEMENTS( resp ) ); |
833 | if (n == (ssize_t) SAL_N_ELEMENTS( resp ) |
834 | && (memcmp( |
835 | resp, "InternalIPC::SendArguments" , |
836 | SAL_N_ELEMENTS( resp ) - 1) == 0)) { |
837 | rtl_uString *pCwdPath = NULL; |
838 | osl_getProcessWorkingDir( &pCwdPath ); |
839 | |
840 | // Then send args |
841 | bSentArgs = send_args( fd, pCwdPath ); |
842 | } |
843 | |
844 | close( fd ); |
845 | } |
846 | #if OSL_DEBUG_LEVEL > 1 |
847 | else |
848 | ustr_debug( "Failed to connect to pipe" , pPipePath ); |
849 | #endif |
850 | } |
851 | |
852 | if ( !bSentArgs ) |
853 | { |
854 | /* we have to prepare for, and exec the binary */ |
855 | int nPercent = 0; |
856 | ChildInfo *info; |
857 | sal_Bool bAllArgs = sal_True; |
858 | sal_Bool bShortWait, bRestart; |
859 | |
860 | /* sanity check pieces */ |
861 | system_checks(); |
862 | |
863 | /* load splash image and create window */ |
864 | if ( !args->bInhibitSplash ) |
865 | { |
866 | splash = splash_create(args->pAppPath, argc, argv); |
867 | } |
868 | |
869 | /* pagein */ |
870 | if (!args->bInhibitPagein) |
871 | exec_pagein (args); |
872 | |
873 | /* javaldx */ |
874 | #if HAVE_FEATURE_JAVA |
875 | if (!args->bInhibitJavaLdx) |
876 | exec_javaldx (args); |
877 | #endif |
878 | |
879 | do |
880 | { |
881 | bRestart = sal_False; |
882 | |
883 | /* fast updates if we have somewhere to update it to */ |
884 | bShortWait = splash ? sal_True : sal_False; |
885 | |
886 | /* Periodically update the splash & the percent according |
887 | to what status_fd says, poll quickly only while starting */ |
888 | info = child_spawn (args, bAllArgs, bShortWait); |
889 | g_pProcess = info->child; |
890 | while (!child_exited_wait (info, bShortWait)) |
891 | { |
892 | ProgressStatus eResult; |
893 | |
894 | splash_draw_progress( splash, nPercent ); |
895 | eResult = read_percent( info, &nPercent ); |
896 | if (eResult != ProgressContinue) |
897 | { |
898 | splash_destroy(splash); |
899 | splash = NULL; |
900 | bShortWait = sal_False; |
901 | } |
902 | |
903 | #if OSL_DEBUG_LEVEL > 1 |
904 | fprintf( stderr, "Polling, result is %s\n" , |
905 | ( eResult == ProgressContinue )? "continue" : |
906 | ( ( eResult == ProgressRestart )? "restart" : "exit" ) ); |
907 | #endif |
908 | } |
909 | |
910 | #if OSL_DEBUG_LEVEL > 1 |
911 | fprintf (stderr, "Exited with code '%d'\n" , child_get_exit_code (info)); |
912 | #endif |
913 | |
914 | status = child_get_exit_code(info); |
915 | g_pProcess = 0; // reset |
916 | switch (status) { |
917 | case EXITHELPER_CRASH_WITH_RESTART: // re-start with just -env: parameters |
918 | #if OSL_DEBUG_LEVEL > 1 |
919 | fprintf (stderr, "oosplash: re-start with just -env: params !\n" ); |
920 | #endif |
921 | bRestart = sal_True; |
922 | bAllArgs = sal_False; |
923 | break; |
924 | case EXITHELPER_NORMAL_RESTART: // re-start with all arguments |
925 | #if OSL_DEBUG_LEVEL > 1 |
926 | fprintf (stderr, "oosplash: re-start with all params !\n" ); |
927 | #endif |
928 | bRestart = sal_True; |
929 | bAllArgs = sal_True; |
930 | break; |
931 | default: |
932 | break; |
933 | } |
934 | |
935 | child_info_destroy (info); |
936 | } while (bRestart); |
937 | } |
938 | |
939 | /* cleanup */ |
940 | if ( pPipePath ) |
941 | rtl_uString_release( pPipePath ); |
942 | args_free (args); |
943 | |
944 | return status; |
945 | } |
946 | |
947 | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |
948 | |