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