1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <sys/stat.h>
33 
34 #ifdef LINUX
35 #define __USE_GNU
36 #endif
37 #include <dlfcn.h>
38 
39 #include "cppuhelper/findsofficepath.h"
40 #include "rtl/string.h"
41 #include "sal/types.h"
42 
43 char const* getPath();
44 char* createCommandName( char* argv0 );
45 
46 const int SEPARATOR = '/';
47 const char* PATHSEPARATOR = ":";
48 
49 
50 /*
51  * The main function implements a loader for applications which use UNO.
52  *
53  * <p>This code runs on the Unix/Linux platforms only.</p>
54  *
55  * <p>The main function detects a UNO installation on the system and adds the
56  * relevant directories of the installation to the LD_LIBRARY_PATH environment
57  * variable. After that, the application process is loaded and started, whereby
58  * the new process inherits the environment of the calling process, including
59  * the modified LD_LIBRARY_PATH environment variable. The application's
60  * executable name must be the same as the name of this executable, prefixed
61  * by '_'.</p>
62  * <p>On MACOSX DYLD_LIBRARY_PATH is used instead of LD_LIBRARY_PATH!<p>
63  *
64  * <p>A UNO installation can be specified by the user by setting the UNO_PATH
65  * environment variable to the program directory of the UNO installation.
66  * If no installation is specified by the user, the default installation on
67  * the system will be taken. The default installation is found from the
68  * PATH environment variable. This requires that the 'soffice' executable or
69  * a symbolic link is in one of the directories listed in the PATH environment
70  * variable.</p>
71  */
72 int main( int argc, char *argv[] )
73 {
74     char const* path;
75     char* cmdname;
76 
77     (void) argc; /* avoid warning about unused parameter */
78 
79     /* get the path of the UNO installation */
80     path = getPath();
81 
82     if ( path != NULL )
83     {
84 #ifdef MACOSX
85         static const char* ENVVARNAME = "DYLD_LIBRARY_PATH";
86 #else
87         static const char* ENVVARNAME = "LD_LIBRARY_PATH";
88 #endif
89         char * libpath;
90         int freeLibpath;
91 
92         char* value;
93         char* envstr;
94         int size;
95 
96         size_t pathlen = strlen(path);
97         struct stat stat;
98         int ret;
99         char * unoinfo = malloc(
100             pathlen + RTL_CONSTASCII_LENGTH("/unoinfo") + 1);
101             /*TODO: overflow */
102         if (unoinfo == NULL) {
103             fprintf(stderr, "Error: out of memory!\n");
104             exit(EXIT_FAILURE);
105         }
106         strcpy(unoinfo, path);
107         strcpy(
108             unoinfo + pathlen,
109             "/unoinfo" + (pathlen == 0 || path[pathlen - 1] != '/' ? 0 : 1));
110         ret = lstat(unoinfo, &stat);
111         free(unoinfo);
112 
113         if (ret == 0) {
114             char * cmd = malloc(
115                 2 * pathlen + RTL_CONSTASCII_LENGTH("/unoinfo c++") + 1);
116                 /*TODO: overflow */
117             char const * p;
118             char * q;
119             FILE * f;
120             size_t n = 1000;
121             size_t old = 0;
122             if (cmd == NULL) {
123                 fprintf(stderr, "Error: out of memory!\n");
124                 exit(EXIT_FAILURE);
125             }
126             p = path;
127             q = cmd;
128             while (*p != '\0') {
129                 *q++ = '\\';
130                 *q++ = *p++;
131             }
132             if (p == path || p[-1] != '/') {
133                 *q++ = '/';
134             }
135             strcpy(q, "unoinfo c++");
136             f = popen(cmd, "r");
137             free(cmd);
138             if (f == NULL)
139             {
140                 fprintf(stderr, "Error: calling unoinfo failed!\n");
141                 exit(EXIT_FAILURE);
142             }
143             libpath = NULL;
144             for (;;) {
145                 size_t m;
146                 libpath = realloc(libpath, n);
147                 if (libpath == NULL) {
148                     fprintf(
149                         stderr,
150                         "Error: out of memory reading unoinfo output!\n");
151                     exit(EXIT_FAILURE);
152                 }
153                 m = fread(libpath + old, 1, n - old - 1, f);
154                 if (m != n - old - 1) {
155                     if (ferror(f)) {
156                         fprintf(stderr, "Error: cannot read unoinfo output!\n");
157                         exit(EXIT_FAILURE);
158                     }
159                     libpath[old + m] = '\0';
160                     break;
161                 }
162                 if (n >= SAL_MAX_SIZE / 2) {
163                     fprintf(
164                         stderr,
165                         "Error: out of memory reading unoinfo output!\n");
166                     exit(EXIT_FAILURE);
167                 }
168                 old = n - 1;
169                 n *= 2;
170             }
171             if (pclose(f) != 0) {
172                 fprintf(stderr, "Error: executing unoinfo failed!\n");
173                 exit(EXIT_FAILURE);
174             }
175             freeLibpath = 1;
176         }
177         else
178         {
179             /* Assume an old OOo 2.x installation without unoinfo: */
180             libpath = (char *) path;
181             freeLibpath = 0;
182         }
183 
184         value = getenv( ENVVARNAME );
185 
186         size = strlen( ENVVARNAME ) + strlen( "=" ) + strlen( libpath ) + 1;
187 		if ( value != NULL )
188             size += strlen( PATHSEPARATOR ) + strlen( value );
189 		envstr = (char*) malloc( size );
190         strcpy( envstr, ENVVARNAME );
191         strcat( envstr, "=" );
192         strcat( envstr, libpath );
193         if ( freeLibpath != 0 )
194         {
195             free( libpath );
196         }
197 		if ( value != NULL )
198 		{
199             strcat( envstr, PATHSEPARATOR );
200             strcat( envstr, value );
201 		}
202         putenv( envstr );
203     }
204     else
205     {
206         fprintf( stderr, "Warning: no UNO installation found!\n" );
207         fflush( stderr );
208     }
209 
210     /* set the executable name for the application process */
211     cmdname = createCommandName( argv[0] );
212     argv[0] = cmdname;
213 
214     /*
215      * create the application process;
216      * if successful, execvp doesn't return to the calling process
217      */
218     execvp( cmdname, argv );
219     fprintf( stderr, "Error: execvp failed!\n" );
220     fflush( stderr );
221 
222     return 0;
223 }
224 
225 /*
226  * Gets the path of a UNO installation.
227  *
228  * @return the installation path or NULL, if no installation was specified or
229  *         found, or if an error occured
230  */
231 char const* getPath()
232 {
233     char const* path = cppuhelper_detail_findSofficePath();
234 
235     if ( path == NULL )
236     {
237         fprintf( stderr, "Warning: getting path from PATH environment "
238                  "variable failed!\n" );
239         fflush( stderr );
240     }
241 
242     return path;
243 }
244 
245 /*
246  * Creates the application's executable file name.
247  *
248  * <p>The application's executable file name is the name of this executable
249  * prefixed by '_'.</p>
250  *
251  * @param argv0 specifies the argv[0] parameter of the main function
252  *
253  * @return the application's executable file name or NULL, if an error occured
254  */
255 char* createCommandName( char* argv0 )
256 {
257     const char* CMDPREFIX = "_";
258     const char* prgname = NULL;
259 
260     char* cmdname = NULL;
261     char* sep = NULL;
262     Dl_info dl_info;
263     int pos;
264 
265     /* get the executable file name from argv0 */
266 	prgname = argv0;
267 
268 	/*
269      * if argv0 doesn't contain an absolute path name, try to get the absolute
270      * path name from dladdr; note that this only works for Solaris, not for
271      * Linux
272      */
273     if ( argv0 != NULL && *argv0 != SEPARATOR &&
274          dladdr( (void*) &createCommandName, &dl_info ) &&
275          dl_info.dli_fname != NULL && *dl_info.dli_fname == SEPARATOR )
276     {
277         prgname = dl_info.dli_fname;
278 	}
279 
280     /* prefix the executable file name by '_' */
281     if ( prgname != NULL )
282 	{
283         cmdname = (char*) malloc( strlen( prgname ) + strlen( CMDPREFIX ) + 1 );
284         sep = strrchr( prgname, SEPARATOR );
285         if ( sep != NULL )
286         {
287 			pos = ++sep - prgname;
288             strncpy( cmdname, prgname, pos );
289             cmdname[ pos ] = '\0';
290             strcat( cmdname, CMDPREFIX );
291             strcat( cmdname, sep );
292         }
293         else
294 		{
295             strcpy( cmdname, CMDPREFIX );
296 			strcat( cmdname, prgname );
297 		}
298 	}
299 
300     return cmdname;
301 }
302