1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <sys/stat.h>
29 
30 #ifdef LINUX
31 #define __USE_GNU
32 #endif
33 #include <dlfcn.h>
34 
35 #include "cppuhelper/findsofficepath.h"
36 #include "rtl/string.h"
37 #include "sal/types.h"
38 
39 char const* getPath();
40 char* createCommandName( char* argv0 );
41 
42 const int SEPARATOR = '/';
43 const char* PATHSEPARATOR = ":";
44 
45 
46 /*
47  * The main function implements a loader for applications which use UNO.
48  *
49  * <p>This code runs on the Unix/Linux platforms only.</p>
50  *
51  * <p>The main function detects a UNO installation on the system and adds the
52  * relevant directories of the installation to the LD_LIBRARY_PATH environment
53  * variable. After that, the application process is loaded and started, whereby
54  * the new process inherits the environment of the calling process, including
55  * the modified LD_LIBRARY_PATH environment variable. The application's
56  * executable name must be the same as the name of this executable, prefixed
57  * by '_'.</p>
58  * <p>On MACOSX DYLD_LIBRARY_PATH is used instead of LD_LIBRARY_PATH!<p>
59  *
60  * <p>A UNO installation can be specified by the user by setting the UNO_PATH
61  * environment variable to the program directory of the UNO installation.
62  * If no installation is specified by the user, the default installation on
63  * the system will be taken. The default installation is found from the
64  * PATH environment variable. This requires that the 'soffice' executable or
65  * a symbolic link is in one of the directories listed in the PATH environment
66  * variable.</p>
67  */
main(int argc,char * argv[])68 int main( int argc, char *argv[] )
69 {
70     char const* path;
71     char* cmdname;
72 
73     (void) argc; /* avoid warning about unused parameter */
74 
75     /* get the path of the UNO installation */
76     path = getPath();
77 
78     if ( path != NULL )
79     {
80 #ifdef MACOSX
81         static const char* ENVVARNAME = "DYLD_LIBRARY_PATH";
82 #else
83         static const char* ENVVARNAME = "LD_LIBRARY_PATH";
84 #endif
85         char * libpath;
86         int freeLibpath;
87 
88         char* value;
89         char* envstr;
90         int size;
91 
92         size_t pathlen = strlen(path);
93         struct stat stat;
94         int ret;
95         char * unoinfo = malloc(
96             pathlen + RTL_CONSTASCII_LENGTH("/unoinfo") + 1);
97             /*TODO: overflow */
98         if (unoinfo == NULL) {
99             fprintf(stderr, "Error: out of memory!\n");
100             exit(EXIT_FAILURE);
101         }
102         strcpy(unoinfo, path);
103         strcpy(
104             unoinfo + pathlen,
105             "/unoinfo" + (pathlen == 0 || path[pathlen - 1] != '/' ? 0 : 1));
106         ret = lstat(unoinfo, &stat);
107         free(unoinfo);
108 
109         if (ret == 0) {
110             char * cmd = malloc(
111                 2 * pathlen + RTL_CONSTASCII_LENGTH("/unoinfo c++") + 1);
112                 /*TODO: overflow */
113             char const * p;
114             char * q;
115             FILE * f;
116             size_t n = 1000;
117             size_t old = 0;
118             if (cmd == NULL) {
119                 fprintf(stderr, "Error: out of memory!\n");
120                 exit(EXIT_FAILURE);
121             }
122             p = path;
123             q = cmd;
124             while (*p != '\0') {
125                 *q++ = '\\';
126                 *q++ = *p++;
127             }
128             if (p == path || p[-1] != '/') {
129                 *q++ = '/';
130             }
131             strcpy(q, "unoinfo c++");
132             f = popen(cmd, "r");
133             free(cmd);
134             if (f == NULL)
135             {
136                 fprintf(stderr, "Error: calling unoinfo failed!\n");
137                 exit(EXIT_FAILURE);
138             }
139             libpath = NULL;
140             for (;;) {
141                 size_t m;
142                 libpath = realloc(libpath, n);
143                 if (libpath == NULL) {
144                     fprintf(
145                         stderr,
146                         "Error: out of memory reading unoinfo output!\n");
147                     exit(EXIT_FAILURE);
148                 }
149                 m = fread(libpath + old, 1, n - old - 1, f);
150                 if (m != n - old - 1) {
151                     if (ferror(f)) {
152                         fprintf(stderr, "Error: cannot read unoinfo output!\n");
153                         exit(EXIT_FAILURE);
154                     }
155                     libpath[old + m] = '\0';
156                     break;
157                 }
158                 if (n >= SAL_MAX_SIZE / 2) {
159                     fprintf(
160                         stderr,
161                         "Error: out of memory reading unoinfo output!\n");
162                     exit(EXIT_FAILURE);
163                 }
164                 old = n - 1;
165                 n *= 2;
166             }
167             if (pclose(f) != 0) {
168                 fprintf(stderr, "Error: executing unoinfo failed!\n");
169                 exit(EXIT_FAILURE);
170             }
171             freeLibpath = 1;
172         }
173         else
174         {
175             /* Assume an old OOo 2.x installation without unoinfo: */
176             libpath = (char *) path;
177             freeLibpath = 0;
178         }
179 
180         value = getenv( ENVVARNAME );
181 
182         // workaround for finding wrong libsqlite3.dylib in the office installation
183         // For MacOS > 10.6 nss uses the system lib -> unresolved symbol _sqlite3_wal_checkpoint
184 #ifdef MACOSX
185         size = strlen( ENVVARNAME ) + strlen( "=/usr/lib:" ) + strlen( libpath ) + 1;
186 #else
187         size = strlen( ENVVARNAME ) + strlen( "=" ) + strlen( libpath ) + 1;
188 #endif
189 		if ( value != NULL )
190             size += strlen( PATHSEPARATOR ) + strlen( value );
191 		envstr = (char*) malloc( size );
192         strcpy( envstr, ENVVARNAME );
193 #ifdef MACOSX
194         strcat( envstr, "=/usr/lib:" );
195 #else
196         strcat( envstr, "=" );
197 #endif
198         strcat( envstr, libpath );
199         if ( freeLibpath != 0 )
200         {
201             free( libpath );
202         }
203 		if ( value != NULL )
204 		{
205             strcat( envstr, PATHSEPARATOR );
206             strcat( envstr, value );
207 		}
208         putenv( envstr );
209 #ifdef MACOSX
210         /* https://bz.apache.org/ooo/show_bug.cgi?id=127965 */
211         value = getenv( "PATH" );
212         if (!strstr ( value, "/usr/local/bin" )) {
213             size = strlen( "PATH" ) + strlen( "=/usr/local/bin" ) + 1;
214             if ( value != NULL )
215                 size += strlen( PATHSEPARATOR ) + strlen( value );
216             envstr = (char*) malloc( size );
217             strcpy( envstr, "PATH=" );
218             if ( value != NULL ) {
219                 strcat( envstr, value);
220                 strcat( envstr, PATHSEPARATOR);
221             }
222             strcat( envstr, "/usr/local/bin" ); /* We are adding at the end */
223             putenv( envstr );
224         }
225 
226         /* https://bz.apache.org/ooo/show_bug.cgi?id=127966 */
227         value = getenv ( "HOME" );
228         if ( value && *value ) {
229           chdir ( value );
230         } else {
231             chdir ( "/tmp" );
232         }
233 #endif
234     }
235     else
236     {
237         fprintf( stderr, "Warning: no office installation found!\n" );
238         fflush( stderr );
239     }
240 
241     /* set the executable name for the application process */
242     cmdname = createCommandName( argv[0] );
243     argv[0] = cmdname;
244 
245     /*
246      * create the application process;
247      * if successful, execvp doesn't return to the calling process
248      */
249     execvp( cmdname, argv );
250     fprintf( stderr, "Error: execvp failed!\n" );
251     fflush( stderr );
252 
253     return 0;
254 }
255 
256 /*
257  * Gets the path of a UNO installation.
258  *
259  * @return the installation path or NULL, if no installation was specified or
260  *         found, or if an error occurred
261  */
getPath()262 char const* getPath()
263 {
264     char const* path = cppuhelper_detail_findSofficePath();
265 
266     if ( path == NULL )
267     {
268         fprintf( stderr, "Warning: getting path from PATH environment variable failed!\n" );
269         fflush( stderr );
270     }
271 
272     return path;
273 }
274 
275 /*
276  * Creates the application's executable file name.
277  *
278  * <p>The application's executable file name is the name of this executable
279  * prefixed by '_'.</p>
280  *
281  * @param argv0 specifies the argv[0] parameter of the main function
282  *
283  * @return the application's executable file name or NULL, if an error occurred
284  */
createCommandName(char * argv0)285 char* createCommandName( char* argv0 )
286 {
287     const char* CMDPREFIX = "_";
288     const char* prgname = NULL;
289 
290     char* cmdname = NULL;
291     char* sep = NULL;
292     Dl_info dl_info;
293     int pos;
294 
295     /* get the executable file name from argv0 */
296 	prgname = argv0;
297 
298 	/*
299      * if argv0 doesn't contain an absolute path name, try to get the absolute
300      * path name from dladdr; note that this only works for Solaris, not for
301      * Linux
302      */
303     if ( argv0 != NULL && *argv0 != SEPARATOR &&
304          dladdr( (void*) &createCommandName, &dl_info ) &&
305          dl_info.dli_fname != NULL && *dl_info.dli_fname == SEPARATOR )
306     {
307         prgname = dl_info.dli_fname;
308 	}
309 
310     /* prefix the executable file name by '_' */
311     if ( prgname != NULL )
312 	{
313         cmdname = (char*) malloc( strlen( prgname ) + strlen( CMDPREFIX ) + 1 );
314         sep = strrchr( prgname, SEPARATOR );
315         if ( sep != NULL )
316         {
317 			pos = ++sep - prgname;
318             strncpy( cmdname, prgname, pos );
319             cmdname[ pos ] = '\0';
320             strcat( cmdname, CMDPREFIX );
321             strcat( cmdname, sep );
322         }
323         else
324 		{
325             strcpy( cmdname, CMDPREFIX );
326 			strcat( cmdname, prgname );
327 		}
328 	}
329 
330     return cmdname;
331 }
332