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