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