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