diff options
Diffstat (limited to 'desktop/unx/source/start.c')
-rwxr-xr-x | desktop/unx/source/start.c | 1030 |
1 files changed, 1030 insertions, 0 deletions
diff --git a/desktop/unx/source/start.c b/desktop/unx/source/start.c new file mode 100755 index 000000000000..4c53c03ef8a0 --- /dev/null +++ b/desktop/unx/source/start.c @@ -0,0 +1,1030 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Version: MPL 1.1 / GPLv3+ / LGPLv3+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Initial Developer of the Original Code is + * Novell, Inc. + * Portions created by the Initial Developer are Copyright (C) 2010 the + * Initial Developer. All Rights Reserved. + * + * Contributor(s): Jan Holesovsky <kendy@novell.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 3 or later (the "GPLv3+"), or + * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"), + * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable + * instead of those above. + */ +#include <signal.h> +#include <unistd.h> +#include <limits.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <sys/un.h> +#include <sys/poll.h> +#include <fcntl.h> +#include <stdio.h> +#include <libgen.h> +#include <string.h> + +#include <osl/nlsupport.h> +#include <osl/process.h> +#include <osl/thread.h> +#include <rtl/bootstrap.h> +#include <rtl/digest.h> +#include <rtl/ustrbuf.h> +#include <sal/main.h> + +#include "args.h" +#include "splashx.h" + +#define IMG_SUFFIX ".png" +#define PIPEDEFAULTPATH "/tmp" +#define PIPEALTERNATEPATH "/var/tmp" + +/* Easier conversions: rtl_uString to rtl_String */ +static rtl_String * +ustr_to_str( rtl_uString *pStr ) +{ + rtl_String *pOut = NULL; + + rtl_uString2String( &pOut, rtl_uString_getStr( pStr ), + rtl_uString_getLength( pStr ), osl_getThreadTextEncoding(), OUSTRING_TO_OSTRING_CVTFLAGS ); + + return pOut; +} + +/* Easier conversions: char * to rtl_uString */ +static rtl_uString * +charp_to_ustr( const char *pStr ) +{ + rtl_uString *pOut = NULL; + + rtl_string2UString( &pOut, pStr, strlen( pStr ), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS ); + + return pOut; +} + +/* Easier debugging of rtl_uString values. */ +#if OSL_DEBUG_LEVEL > 0 +static void +ustr_debug( const char *pMessage, rtl_uString *pStr ) +{ + rtl_String *pOut = ustr_to_str( pStr ); + + fprintf( stderr, "%s: %s\n", pMessage, rtl_string_getStr( pOut ) ); + + rtl_string_release( pOut ); + return; +} +#else +#define ustr_debug( a, b ) {} +#endif + +typedef struct { + int status_fd; + oslProcess child; +} ChildInfo; + +static int +child_info_get_status_fd (ChildInfo *info) +{ + return info->status_fd; +} + +static void +child_info_destroy (ChildInfo *info) +{ + close (info->status_fd); + osl_freeProcessHandle (info->child); + free (info); +} + +static ChildInfo * +child_spawn ( Args *args, sal_Bool bAllArgs, sal_Bool bWithStatus ) +{ + rtl_uString *pApp = NULL, *pTmp = NULL; + rtl_uString **ppArgs; + sal_uInt32 nArgs, i; + char buffer[64]; + ChildInfo *info; + int status_pipe[2]; + oslProcessError nError; + + info = calloc (1, sizeof (ChildInfo)); + + /* create pipe */ + if ( pipe( status_pipe ) < 0 ) + { + fprintf( stderr, "ERROR: no file handles\n"); + exit( 1 ); + } + info->status_fd = status_pipe[0]; + + /* application name */ + rtl_uString_newFromAscii( &pApp, "file://" ); + rtl_uString_newConcat( &pApp, pApp, args->pAppPath ); + rtl_uString_newFromAscii( &pTmp, "/soffice.bin" ); + rtl_uString_newConcat( &pApp, pApp, pTmp ); + rtl_uString_release( pTmp ); + pTmp = NULL; + + /* copy args */ + nArgs = bAllArgs ? args->nArgsTotal : args->nArgsEnv; + ppArgs = (rtl_uString **)calloc( nArgs + 1, sizeof( rtl_uString* ) ); + for ( i = 0; i < nArgs; ++i ) + ppArgs[i] = args->ppArgs[i]; + + if( bWithStatus ) + { + /* add the pipe arg */ + snprintf (buffer, 63, "--splash-pipe=%d", status_pipe[1]); + rtl_uString_newFromAscii( &pTmp, buffer ); + ppArgs[nArgs] = pTmp; + ++nArgs; + } + + /* start the main process */ + nError = osl_executeProcess( pApp, ppArgs, nArgs, + osl_Process_NORMAL, + NULL, + NULL, + NULL, 0, + &info->child ); + + if (pTmp) + rtl_uString_release( pTmp ); + free (ppArgs); + + if ( nError != osl_Process_E_None ) + { + fprintf( stderr, "ERROR %d forking process", nError ); + ustr_debug( "", pApp ); + rtl_uString_release( pApp ); + _exit (1); + } + + rtl_uString_release( pApp ); + close( status_pipe[1] ); + + return info; +} + +static sal_Bool +child_exited_wait (ChildInfo *info, sal_Bool bShortWait) +{ + TimeValue t = { 0, 250 /* ms */ * 1000 * 1000 }; + if (!bShortWait) + t.Seconds = 1024; + return osl_joinProcessWithTimeout (info->child, &t) != osl_Process_E_TimedOut; +} + +static int +child_get_exit_code (ChildInfo *info) +{ + oslProcessInfo inf; + + inf.Code = -1; + inf.Size = sizeof (inf); + if (osl_getProcessInfo (info->child, osl_Process_EXITCODE, &inf) != osl_Process_E_None) + { + fprintf (stderr, "Warning: failed to fetch libreoffice exit status\n"); + return -1; + } + return inf.Code; +} + +typedef enum { ProgressContinue, ProgressRestart, ProgressExit } ProgressStatus; + +/* Path of the application. */ +static rtl_uString * +get_app_path( const char *pAppExec ) +{ + char pRealPath[PATH_MAX]; + rtl_uString *pResult; + + char *pOrigPath = strdup( pAppExec ); + char *pPath = dirname( pOrigPath ); + + realpath( pPath, pRealPath ); + pResult = charp_to_ustr( pRealPath ); + free( pOrigPath ); + + return pResult; +} + +/* Compute the OOo md5 hash from 'pText' */ +static rtl_uString * +get_md5hash( rtl_uString *pText ) +{ + rtl_uString *pResult = NULL; + sal_Int32 nCapacity = 100; + +#if OSL_DEBUG_LEVEL > 0 + fprintf (stderr, "Generate pipe md5 for '%s'\n", ustr_to_str (pText)->buffer); +#endif + + if ( !pText ) + return NULL; + + unsigned char *pData = (unsigned char *)rtl_uString_getStr( pText ); + sal_uInt32 nSize = rtl_uString_getLength( pText ) * sizeof( sal_Unicode ); + if ( !pData ) + return NULL; + + rtlDigest digest = rtl_digest_create( rtl_Digest_AlgorithmMD5 ); + if ( digest == 0 ) + return NULL; + + sal_uInt32 md5_key_len = rtl_digest_queryLength( digest ); + sal_uInt8 *md5_buf = (sal_uInt8 *)calloc( md5_key_len, sizeof( sal_uInt8 ) ); + + rtl_digest_init( digest, pData , nSize ); + rtl_digest_update( digest, pData, nSize ); + rtl_digest_get( digest, md5_buf, md5_key_len ); + rtl_digest_destroy( digest ); + + /* create hex-value string from the MD5 value to keep + the string size minimal */ + rtl_uString_new_WithLength( &pResult, nCapacity ); + sal_uInt32 i = 0; + for ( ; i < md5_key_len; ++i ) + { + char val[3]; + snprintf( val, 3, "%x", md5_buf[i] ); /* sic! we ignore some of the 0's */ + + rtl_uStringbuffer_insert_ascii( &pResult, &nCapacity, rtl_uString_getLength( pResult ), + val, strlen( val ) ); + } + + /* cleanup */ + free( md5_buf ); + + return pResult; +} + +/* Construct the pipe name */ +static rtl_uString * +get_pipe_path( rtl_uString *pAppPath ) +{ + rtl_uString *pPath = NULL, *pTmp = NULL, *pUserInstallation = NULL; + rtl_uString *pResult = NULL, *pBasePath = NULL, *pAbsUserInstallation = NULL; + + /* setup bootstrap filename */ + rtl_uString_newFromAscii( &pPath, "file://" ); + rtl_uString_newConcat( &pPath, pPath, pAppPath ); + rtl_uString_newFromAscii( &pTmp, "/" ); + rtl_uString_newConcat( &pPath, pPath, pTmp ); + rtl_uString_newFromAscii( &pTmp, SAL_CONFIGFILE( "bootstrap" ) ); + rtl_uString_newConcat( &pPath, pPath, pTmp ); + + ustr_debug( "bootstap", pPath ); + + /* read userinstallation value */ + rtlBootstrapHandle handle = rtl_bootstrap_args_open( pPath ); + + rtl_uString_newFromAscii( &pTmp, "UserInstallation" ); + rtl_bootstrap_get_from_handle( handle, pTmp, &pUserInstallation, NULL ); + + rtl_bootstrap_args_close( handle ); + + /* turn it into an absolute path - unwinding symlinks etc. */ + if ( osl_getProcessWorkingDir (&pBasePath) || + osl_getAbsoluteFileURL( pBasePath, pUserInstallation, &pAbsUserInstallation ) ) + rtl_uString_newFromString (&pAbsUserInstallation, pUserInstallation); + + /* create the pipe name */ + ustr_debug( "user installation", pAbsUserInstallation ); + rtl_uString *pMd5hash = get_md5hash( pAbsUserInstallation ); + if ( !pMd5hash ) + rtl_uString_new( &pMd5hash ); + + if ( access( PIPEDEFAULTPATH, R_OK|W_OK ) == 0 ) + rtl_uString_newFromAscii( &pResult, PIPEDEFAULTPATH ); + else + rtl_uString_newFromAscii( &pResult, PIPEALTERNATEPATH ); + + rtl_uString_newFromAscii( &pTmp, "/OSL_PIPE_" ); + rtl_uString_newConcat( &pResult, pResult, pTmp ); + + sal_Unicode pUnicode[RTL_USTR_MAX_VALUEOFINT32]; + rtl_ustr_valueOfInt32( pUnicode, (int)getuid(), 10 ); + rtl_uString_newFromStr( &pTmp, pUnicode ); + rtl_uString_newConcat( &pResult, pResult, pTmp ); + + rtl_uString_newFromAscii( &pTmp, "_SingleOfficeIPC_" ); + rtl_uString_newConcat( &pResult, pResult, pTmp ); + + rtl_uString_newConcat( &pResult, pResult, pMd5hash ); + + ustr_debug( "result", pResult ); + + /* cleanup */ + rtl_uString_release( pMd5hash ); + rtl_uString_release( pPath ); + rtl_uString_release( pTmp ); + rtl_uString_release( pBasePath ); + rtl_uString_release( pUserInstallation ); + rtl_uString_release( pAbsUserInstallation ); + + return pResult; +} + +/* Get fd of the pipe of the already running OOo. */ +static int +connect_pipe( rtl_uString *pPipePath ) +{ + int fd; + size_t len; + struct sockaddr_un addr; + + rtl_String *pPipeStr = ustr_to_str( pPipePath ); + + memset( &addr, 0, sizeof( addr ) ); + + if ( ( fd = socket( AF_UNIX, SOCK_STREAM, 0 ) ) < 0 ) + return fd; + + fcntl( fd, F_SETFD, FD_CLOEXEC ); + + addr.sun_family = AF_UNIX; + strncpy( addr.sun_path, rtl_string_getStr( pPipeStr ), sizeof( addr.sun_path ) ); + rtl_string_release( pPipeStr ); + +/* cut / paste from osl's pipe.c */ +#if defined(FREEBSD) + len = SUN_LEN( &addr ); +#else + len = sizeof( addr ); +#endif + + if ( connect( fd, (struct sockaddr *)&addr, len ) < 0 ) + return -1; + + return fd; +} + +/* Escape: "," -> "\\,", "\0" -> "\\0", "\\" -> "\\\\" */ +static rtl_uString * +escape_path( rtl_uString *pToEscape ) +{ + rtl_uString *pBuffer = NULL; + sal_Int32 nCapacity = 1000; + + rtl_uString_new_WithLength( &pBuffer, nCapacity ); + + sal_Int32 i = 0; + sal_Int32 nEscapeLength = rtl_uString_getLength( pToEscape ); + for ( ; i < nEscapeLength; ++i ) + { + sal_Unicode c = pToEscape->buffer[i]; + switch ( c ) + { + case (sal_Unicode)'\0': + rtl_uStringbuffer_insert_ascii( &pBuffer, &nCapacity, + rtl_uString_getLength( pBuffer ), + RTL_CONSTASCII_STRINGPARAM( "\\0" ) ); + break; + case (sal_Unicode)',': + rtl_uStringbuffer_insert_ascii( &pBuffer, &nCapacity, + rtl_uString_getLength( pBuffer ), + RTL_CONSTASCII_STRINGPARAM( "\\," ) ); + break; + case (sal_Unicode)'\\': + rtl_uStringbuffer_insert_ascii( &pBuffer, &nCapacity, + rtl_uString_getLength( pBuffer ), + RTL_CONSTASCII_STRINGPARAM( "\\\\" ) ); + break; + default: + rtl_uStringbuffer_insert( &pBuffer, &nCapacity, + rtl_uString_getLength( pBuffer ), + &c, 1 ); + } + } + + return pBuffer; +} + +/* Send args to the OOo instance (using the 'fd' file descriptor) */ +static sal_Bool +send_args( int fd, rtl_uString *pCwdPath ) +{ + rtl_uString *pBuffer = NULL, *pTmp = NULL; + sal_Int32 nCapacity = 1000; + rtl_String *pOut = NULL; + sal_Bool bResult; + size_t nLen; + rtl_uString *pEscapedCwdPath = escape_path( pCwdPath ); + + rtl_uString_new_WithLength( &pBuffer, nCapacity ); + rtl_uString_new( &pTmp ); + + rtl_uStringbuffer_insert_ascii( &pBuffer, &nCapacity, + rtl_uString_getLength( pBuffer ), + RTL_CONSTASCII_STRINGPARAM( "InternalIPC::Arguments" ) ); + + if ( rtl_uString_getLength( pEscapedCwdPath ) ) + { + rtl_uStringbuffer_insert_ascii( &pBuffer, &nCapacity, + rtl_uString_getLength( pBuffer ), + RTL_CONSTASCII_STRINGPARAM( "1" ) ); + rtl_uStringbuffer_insert( &pBuffer, &nCapacity, + rtl_uString_getLength( pBuffer ), + rtl_uString_getStr( pEscapedCwdPath ), + rtl_uString_getLength( pEscapedCwdPath ) ); + } + else + rtl_uStringbuffer_insert_ascii( &pBuffer, &nCapacity, + rtl_uString_getLength( pBuffer ), + RTL_CONSTASCII_STRINGPARAM( "0" ) ); + + sal_Bool bDontConvertNext = sal_False; + sal_uInt32 nArg; + sal_uInt32 nArgCount = osl_getCommandArgCount(); + for ( nArg = 0; nArg < nArgCount; ++nArg ) + { + rtl_uStringbuffer_insert_ascii( &pBuffer, &nCapacity, + rtl_uString_getLength( pBuffer ), + ",", 1 ); + + osl_getCommandArg( nArg, &pTmp ); + + // this is not a param, we have to prepend filenames with file:// + // FIXME: improve the check + if ( ( pTmp->buffer[0] != (sal_Unicode)'-' ) ) + { + sal_Int32 nFirstColon = rtl_ustr_indexOfChar_WithLength( pTmp->buffer, pTmp->length, ':' ); + sal_Int32 nFirstSlash = rtl_ustr_indexOfChar_WithLength( pTmp->buffer, pTmp->length, '/' ); + + // check that pTmp is not an URI yet + // note ".uno" ".slot" & "vnd.sun.star.script" are special urls that + // don't expect a following '/' + + const char* schemes[] = { "slot:", ".uno:", "vnd.sun.star.script:" }; + sal_Bool bIsSpecialURL = sal_False; + int index = 0; + int len = SAL_N_ELEMENTS(schemes); + for ( ; index < len; ++index ) + { + if ( rtl_ustr_indexOfAscii_WithLength( pTmp->buffer + , pTmp->length , schemes[ index ], strlen(schemes[ index ] )) == 0 ) + { + bIsSpecialURL = sal_True; + break; + } + } + + if ( !bIsSpecialURL && ( nFirstColon < 1 || ( nFirstSlash != nFirstColon + 1 ) ) ) + { + // some of the switches (currently just -pt) don't want to + // have the filenames as URIs + if ( !bDontConvertNext ) + osl_getAbsoluteFileURL( pCwdPath, pTmp, &pTmp ); + } + } + + // don't convert filenames with some of the switches + // (currently just -pt) + bDontConvertNext = !rtl_ustr_ascii_compareIgnoreAsciiCase( pTmp->buffer, "-pt" ); + + rtl_uString *pEscapedTmp = escape_path( pTmp ); + + rtl_uStringbuffer_insert( &pBuffer, &nCapacity, + rtl_uString_getLength( pBuffer ), + rtl_uString_getStr( pEscapedTmp ), + rtl_uString_getLength( pEscapedTmp ) ); + + rtl_uString_release( pEscapedTmp ); + } + + ustr_debug( "Pass args", pBuffer ); + + pOut = ustr_to_str( pBuffer ); + + nLen = rtl_string_getLength( pOut ) + 1; + bResult = ( write( fd, rtl_string_getStr( pOut ), nLen ) == (ssize_t) nLen ); + + /* cleanup */ + rtl_uString_release( pEscapedCwdPath ); + rtl_uString_release( pBuffer ); + rtl_uString_release( pTmp ); + rtl_string_release( pOut ); + + return bResult; +} + +static void +load_splash_image( rtl_uString *pUAppPath ) +{ + char *pBuffer, *pSuffix, *pLocale; + int nLocSize; + rtl_Locale *pLoc = NULL; + rtl_String *pLang, *pCountry, *pAppPath; + + osl_getProcessLocale (&pLoc); + pLang = ustr_to_str (pLoc->Language); + pCountry = ustr_to_str (pLoc->Country); + + nLocSize = strlen (pLang->buffer) + strlen (pCountry->buffer) + 8; + pLocale = malloc (nLocSize); + pLocale[0] = '-'; + strcpy (pLocale + 1, pLang->buffer); + strcat (pLocale, "_"); + strcat (pLocale, pCountry->buffer); + + rtl_string_release( pCountry ); + rtl_string_release( pLang ); + + pAppPath = ustr_to_str (pUAppPath); + pBuffer = malloc (pAppPath->length + nLocSize + 256); + strcpy (pBuffer, pAppPath->buffer); + pSuffix = pBuffer + pAppPath->length; + rtl_string_release( pAppPath ); + + strcpy (pSuffix, "/edition/intro"); + strcat (pSuffix, pLocale); + strcat (pSuffix, IMG_SUFFIX); + if ( splash_load_bmp( pBuffer ) ) + goto cleanup; + + strcpy (pSuffix, "/edition/intro" IMG_SUFFIX); + if ( splash_load_bmp( pBuffer ) ) + goto cleanup; + + strcpy (pSuffix, "/intro"); + strcat (pSuffix, pLocale); + strcat (pSuffix, IMG_SUFFIX); + if ( splash_load_bmp( pBuffer ) ) + goto cleanup; + + strcpy (pSuffix, "/intro" IMG_SUFFIX); + if ( splash_load_bmp( pBuffer ) ) + goto cleanup; + + fprintf (stderr, "Failed to find intro image\n"); + + cleanup: + free (pLocale); + free (pBuffer); +} + +/* Fill 'array' with values of the key 'name'. + Its value is a comma delimited list of integers */ +static void +get_bootstrap_value( int *array, int size, rtlBootstrapHandle handle, const char *name ) +{ + rtl_uString *pKey = NULL, *pValue = NULL; + + /* get the value from the ini file */ + rtl_uString_newFromAscii( &pKey, name ); + rtl_bootstrap_get_from_handle( handle, pKey, &pValue, NULL ); + + /* the value is several numbers delimited by ',' - parse it */ + if ( rtl_uString_getLength( pValue ) > 0 ) + { + rtl_uString *pToken = NULL; + int i = 0; + sal_Int32 nIndex = 0; + for ( ; ( nIndex >= 0 ) && ( i < size ); ++i ) + { + nIndex = rtl_uString_getToken( &pToken, pValue, 0, ',', nIndex ); + array[i] = rtl_ustr_toInt32( rtl_uString_getStr( pToken ), 10 ); + } + + rtl_uString_release( pToken ); + } + + /* cleanup */ + rtl_uString_release( pKey ); + rtl_uString_release( pValue ); +} + +/* Load the colors and size of the splash. */ +static void +load_splash_defaults( rtl_uString *pAppPath, sal_Bool *bNoDefaults ) +{ + rtl_uString *pSettings = NULL, *pTmp = NULL; + rtlBootstrapHandle handle; + + /* costruct the sofficerc file location */ + rtl_uString_newFromAscii( &pSettings, "file://" ); + rtl_uString_newConcat( &pSettings, pSettings, pAppPath ); + rtl_uString_newFromAscii( &pTmp, "/" ); + rtl_uString_newConcat( &pSettings, pSettings, pTmp ); + rtl_uString_newFromAscii( &pTmp, SAL_CONFIGFILE( "soffice" ) ); + rtl_uString_newConcat( &pSettings, pSettings, pTmp ); + + /* use it as the bootstrap file */ + handle = rtl_bootstrap_args_open( pSettings ); + + int logo[1] = { -1 }, + bar[3] = { -1, -1, -1 }, + frame[3] = { -1, -1, -1 }, + pos[2] = { -1, -1 }, + size[2] = { -1, -1 }; + + /* get the values */ + get_bootstrap_value( logo, 1, handle, "Logo" ); + get_bootstrap_value( bar, 3, handle, "ProgressBarColor" ); + get_bootstrap_value( frame, 3, handle, "ProgressFrameColor" ); + get_bootstrap_value( pos, 2, handle, "ProgressPosition" ); + get_bootstrap_value( size, 2, handle, "ProgressSize" ); + + if ( logo[0] == 0 ) + *bNoDefaults = sal_True; + + splash_setup( bar, frame, pos[0], pos[1], size[0], size[1] ); + + /* cleanup */ + rtl_bootstrap_args_close( handle ); + rtl_uString_release( pSettings ); + rtl_uString_release( pTmp ); +} + +#define BUFFER_LEN 255 + +/* Read the percent to show in splash. */ +static ProgressStatus +read_percent( ChildInfo *info, int *pPercent ) +{ + static char pBuffer[BUFFER_LEN + 1]; + static char *pNext = pBuffer; + static ssize_t nRead = 0; + + char *pBegin; + char *pIter; + + /* from the last call */ + int nNotProcessed = nRead - ( pNext - pBuffer ); + if ( nNotProcessed >= BUFFER_LEN ) + return sal_False; + + memmove( pBuffer, pNext, nNotProcessed ); + + /* read data */ + nRead = read( child_info_get_status_fd (info), + pBuffer + nNotProcessed, BUFFER_LEN - nNotProcessed ); + if ( nRead < 0 ) + return sal_False; + + nRead += nNotProcessed; + pBuffer[nRead] = '\0'; + + /* skip old data */ + pBegin = pBuffer; + pNext = pBuffer; + for ( pIter = pBuffer; *pIter; ++pIter ) + { + if ( *pIter == '\n' ) + { + pBegin = pNext; + pNext = pIter + 1; + } + } + +#if OSL_DEBUG_LEVEL > 0 + fprintf( stderr, "Got status: %s\n", pBegin ); +#endif + if ( !strncasecmp( pBegin, "end", 3 ) ) + return ProgressExit; + else if ( !strncasecmp( pBegin, "restart", 7 ) ) + return ProgressRestart; + else if ( sscanf( pBegin, "%d%%", pPercent ) ) + return ProgressContinue; + + /* unexpected - let's exit the splash to be safe */ + return ProgressExit; +} + +/* Simple system check. */ +static void +system_checks( void ) +{ +#ifdef LINUX + struct stat buf; + + /* check proc is mounted - lots of things fail otherwise */ + if ( stat( "/proc/version", &buf ) != 0 ) + { + fprintf( stderr, "ERROR: /proc not mounted - LibreOffice is unlikely to work well if at all" ); + exit( 1 ); + } +#endif +} + +/* re-use the pagein code */ +extern int pagein_execute (int argc, char **argv); + +#define REL_PATH "/../basis-link/program" +static char *build_pagein_path (Args *args, const char *pagein_name) +{ + char *path; + rtl_String *app_path; + + app_path = ustr_to_str (args->pAppPath); + path = malloc (app_path->length + strlen (pagein_name) + sizeof (REL_PATH) + 8); + strcpy (path, "@"); + strcpy (path + 1, rtl_string_getStr (app_path)); + strcat (path, "/../basis-link/program/"); + strcat (path, pagein_name); + + rtl_string_release( app_path ); + + return path; +} + +void +exec_pagein (Args *args) +{ +// no pagein for the while on OSX +#ifdef MACOSX + (void)args; +#else + char *argv[3]; + + /* don't use -L - since that does a chdir that breaks relative paths */ + argv[0] = "dummy-pagein"; + argv[1] = build_pagein_path (args, "pagein-common"); + if (args->pPageinType) { + argv[2] = build_pagein_path (args, args->pPageinType); + } else + argv[2] = NULL; + + pagein_execute (args->pPageinType ? 3 : 2, argv); + + if (argv[2]) + free (argv[2]); + free (argv[1]); +#endif +} + +static void extend_library_path (const char *new_element) +{ + rtl_uString *pEnvName=NULL, *pOrigEnvVar=NULL, *pNewEnvVar=NULL; + const char *pathname; +#ifdef AIX + pathname = "LIBPATH"; +#else + pathname = "LD_LIBRARY_PATH"; +#endif + + rtl_uString_newFromAscii( &pEnvName, pathname ); + rtl_uString_newFromAscii( &pNewEnvVar, new_element ); + + osl_getEnvironment( pEnvName, &pOrigEnvVar ); + if (pOrigEnvVar && pOrigEnvVar->length) + { + rtl_uString *pDelim = NULL; + rtl_uString_newFromAscii( &pDelim, ":" ); + rtl_uString_newConcat( &pNewEnvVar, pNewEnvVar, pDelim ); + rtl_uString_newConcat( &pNewEnvVar, pNewEnvVar, pOrigEnvVar ); + rtl_uString_release( pDelim ); + } + + osl_setEnvironment( pEnvName, pNewEnvVar ); + + if (pOrigEnvVar) + rtl_uString_release( pOrigEnvVar ); + rtl_uString_release( pNewEnvVar ); + rtl_uString_release( pEnvName ); +} + +static void +exec_javaldx (Args *args) +{ + char newpath[4096]; + sal_uInt32 nArgs; + rtl_uString *pApp; + rtl_uString **ppArgs; + rtl_uString *pTmp, *pTmp2; + rtl_uString *pEnvironment[1] = { NULL }; + + ppArgs = (rtl_uString **)calloc( args->nArgsEnv + 2, sizeof( rtl_uString* ) ); + + for ( nArgs = 0; nArgs < args->nArgsEnv; ++nArgs ) + ppArgs[nArgs] = args->ppArgs[nArgs]; + + /* Use absolute path to redirectrc */ + pTmp = NULL; + rtl_uString_newFromAscii( &pTmp, "-env:INIFILENAME=vnd.sun.star.pathname:" ); + rtl_uString_newConcat( &pTmp, pTmp, args->pAppPath ); + pTmp2 = NULL; + rtl_uString_newFromAscii( &pTmp2, "/redirectrc" ); + rtl_uString_newConcat( &pTmp, pTmp, pTmp2 ); + ppArgs[nArgs] = pTmp; + rtl_uString_release (pTmp2); + nArgs++; + + oslProcess javaldx = NULL; + oslFileHandle fileOut= 0; + oslProcessError err; + + /* And also to javaldx */ + pApp = NULL; + rtl_uString_newFromAscii( &pApp, "file://" ); + rtl_uString_newConcat( &pApp, pApp, args->pAppPath ); + pTmp = NULL; + rtl_uString_newFromAscii( &pTmp, "/../ure/bin/javaldx" ); + rtl_uString_newConcat( &pApp, pApp, pTmp ); + rtl_uString_release( pTmp ); + + /* unset to avoid bogus console output */ + rtl_uString_newFromAscii( &pEnvironment[0], "G_SLICE" ); + + err = osl_executeProcess_WithRedirectedIO( pApp, ppArgs, nArgs, + osl_Process_NORMAL, + NULL, // security + NULL, // work dir + pEnvironment, 1, + &javaldx, // process handle + NULL, + &fileOut, + NULL); + + rtl_uString_release( pEnvironment[0] ); + rtl_uString_release( ppArgs[nArgs-1] ); + rtl_uString_release( pApp ); + free( ppArgs ); + + if( err != osl_Process_E_None) + { + fprintf (stderr, "Warning: failed to launch javaldx - java may not fuction correctly\n"); + return; + } else { + char *chomp; + sal_uInt64 bytes_read; + + /* Magically osl_readLine doesn't work with pipes with E_SPIPE - so be this lame instead: */ + while (osl_readFile (fileOut, newpath, SAL_N_ELEMENTS (newpath), &bytes_read) == osl_File_E_INTR); + + if (bytes_read <= 0) { + fprintf (stderr, "Warning: failed to read path from javaldx\n"); + return; + } + newpath[bytes_read] = '\0'; + + if ((chomp = strstr (newpath, "\n"))) + *chomp = '\0'; + } + +#if OSL_DEBUG_LEVEL > 0 + fprintf (stderr, "Adding javaldx path of '%s'\n", newpath); +#endif + extend_library_path (newpath); + + osl_freeProcessHandle(javaldx); +} + +SAL_IMPLEMENT_MAIN_WITH_ARGS( argc, argv ) +{ + int fd = 0; + sal_Bool bSentArgs = sal_False; + const char* pUsePlugin; + rtl_uString *pPipePath = NULL; + Args *args; + + /* turn SIGPIPE into an error */ + signal( SIGPIPE, SIG_IGN ); + + args = args_parse (); + args->pAppPath = get_app_path( argv[0] ); + if ( !args->pAppPath ) + { + fprintf( stderr, "ERROR: Can't read app link\n" ); + exit( 1 ); + } + ustr_debug( "App path", args->pAppPath ); + +#ifndef ENABLE_QUICKSTART_LIBPNG + /* we can't load and render it anyway */ + args->bInhibitSplash = sal_True; +#endif + + pUsePlugin = getenv( "SAL_USE_VCLPLUGIN" ); + if ( pUsePlugin && !strcmp(pUsePlugin, "svp") ) + args->bInhibitSplash = sal_True; + + pPipePath = get_pipe_path( args->pAppPath ); + + if ( ( fd = connect_pipe( pPipePath ) ) >= 0 ) + { + rtl_uString *pCwdPath = NULL; + osl_getProcessWorkingDir( &pCwdPath ); + + bSentArgs = send_args( fd, pCwdPath ); + + close( fd ); + } +#if OSL_DEBUG_LEVEL > 0 + else + ustr_debug( "Failed to connect to pipe", pPipePath ); +#endif + + if ( !bSentArgs ) + { + /* we have to prepare for, and exec the binary */ + int nPercent = 0; + ChildInfo *info; + sal_Bool bAllArgs = sal_True; + sal_Bool bHaveWindow = sal_False; + sal_Bool bShortWait, bRestart; + + /* sanity check pieces */ + system_checks(); + + /* load splash image and create window */ + if ( !args->bInhibitSplash ) + { + sal_Bool bNoDefaults = sal_False; + load_splash_image( args->pAppPath ); + load_splash_defaults( args->pAppPath, &bNoDefaults ); + + if (!bNoDefaults && + ( bHaveWindow = splash_create_window( argc, argv ) ) ) + splash_draw_progress( 0 ); + } + + /* pagein */ + if (!args->bInhibitJavaLdx) + exec_pagein (args); + + /* javaldx */ + if (!args->bInhibitJavaLdx) + exec_javaldx (args); + + do { + bRestart = sal_False; + + /* fast updates if we have somewhere to update it to */ + bShortWait = bHaveWindow; + + /* Periodically update the splash & the percent according + to what status_fd says, poll quickly only while starting */ + info = child_spawn (args, bAllArgs, bShortWait); + while (!child_exited_wait (info, bShortWait)) + { + ProgressStatus eResult; + + splash_draw_progress( nPercent ); + eResult = read_percent( info, &nPercent ); + if (eResult != ProgressContinue) + { + splash_close_window (); + bShortWait = sal_False; + } + +#if OSL_DEBUG_LEVEL > 0 + fprintf( stderr, "Polling, result is %s\n", + ( eResult == ProgressContinue )? "continue" : + ( ( eResult == ProgressRestart )? "restart" : "exit" ) ); +#endif + } + +#if OSL_DEBUG_LEVEL > 0 + fprintf (stderr, "Exited with code '%d'\n", child_get_exit_code (info)); +#endif + + switch (child_get_exit_code (info)) { + case 79: // re-start with just -env: parameters +#if OSL_DEBUG_LEVEL > 0 + fprintf (stderr, "oosplash: re-start with just -env: params !\n"); +#endif + bRestart = sal_True; + bAllArgs = sal_False; + break; + case 81: // re-start with all arguments +#if OSL_DEBUG_LEVEL > 0 + fprintf (stderr, "oosplash: re-start with all params !\n"); +#endif + bRestart = sal_True; + bAllArgs = sal_True; + break; + default: + break; + } + + child_info_destroy (info); + } while (bRestart); + } + + /* cleanup */ + rtl_uString_release( pPipePath ); + args_free (args); + + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |