1 /*******************************************************************************
2  * Copyright (c) 2006, 2015 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Andrew Niefer
14  *     Red Hat, Inc - Bug 379102 - Prevent running Eclipse as root (optionally)
15  *     Rapicorp, Inc - Bug 461728 - [Mac] Allow users to specify values in eclipse.ini outside of the installation
16  *******************************************************************************/
17 
18 #include "eclipseUnicode.h"
19 #include "eclipseCommon.h"
20 #include "eclipseConfig.h"
21 
22 #ifdef _WIN32
23 #include <direct.h>
24 #else
25 #include <unistd.h>
26 #endif
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <locale.h>
32 #include <sys/stat.h>
33 
34 #include "eclipse-memcpy.h"
35 
36 static _TCHAR* libraryMsg =
37 _T_ECLIPSE("The %s executable launcher was unable to locate its \n\
38 companion shared library.");
39 
40 static _TCHAR* entryMsg =
41 _T_ECLIPSE("There was a problem loading the shared library and \n\
42 finding the entry point.");
43 
44 static _TCHAR* rootMsg =
45 _T_ECLIPSE("The %s executable launcher is configured to not start with \n\
46 administrative privileges.");
47 
48 #define NAME         _T_ECLIPSE("-name")
49 #define VMARGS       _T_ECLIPSE("-vmargs")		/* special option processing required */
50 /* New arguments have the form --launcher.<arg> to avoid collisions */
51 #define LIBRARY		  _T_ECLIPSE("--launcher.library")
52 #define SUPRESSERRORS _T_ECLIPSE("--launcher.suppressErrors")
53 #define INI			  _T_ECLIPSE("--launcher.ini")
54 #define PROTECT	      _T_ECLIPSE("-protect")	/* This argument is also handled in eclipse.c for Mac specific processing */
55 #define ROOT		  _T_ECLIPSE("root")		/* the only level of protection we care now */
56 
57 /* this typedef must match the run method in eclipse.c */
58 typedef int (*RunMethod)(int argc, _TCHAR* argv[], _TCHAR* vmArgs[]);
59 typedef void (*SetInitialArgs)(int argc, _TCHAR*argv[], _TCHAR* library);
60 
61 static _TCHAR*  name          = NULL;			/* program name */
62 static _TCHAR** userVMarg     = NULL;     		/* user specific args for the Java VM */
63 static _TCHAR*  programDir	  = NULL;			/* directory where program resides */
64 static _TCHAR*  officialName  = NULL;
65 static int      suppressErrors = 0;				/* supress error dialogs */
66 static int      protectRoot      = 0;				/* check if launcher was run as root, currently works only on Linux/UNIX platforms */
67 
68 static int 	 	createUserArgs(int configArgc, _TCHAR **configArgv, int *argc, _TCHAR ***argv);
69 static void  	parseArgs( int* argc, _TCHAR* argv[], int handleVMArgs );
70 static _TCHAR* 	getDefaultOfficialName(_TCHAR* program);
71 static _TCHAR*  findProgram(_TCHAR* argv[]);
72 static _TCHAR*  findLibrary(_TCHAR* library, _TCHAR* program);
73 static _TCHAR*  checkForIni(int argc, _TCHAR* argv[]);
74 static _TCHAR*  getDirFromProgram(_TCHAR* program);
75 static int  isRoot();
76 
77 static int initialArgc;
78 static _TCHAR** initialArgv;
79 
80 _TCHAR* eclipseLibrary = NULL; /* path to the eclipse shared library */
81 
82 #ifdef UNICODE
83 extern int main(int, char**);
84 int mainW(int, wchar_t**);
wmain(int argc,wchar_t ** argv)85 int wmain( int argc, wchar_t** argv ) {
86 	return mainW(argc, argv);
87 }
88 
main(int argc,char * argv[])89 int main(int argc, char* argv[]) {
90 	/*
91 	* Run the UNICODE version, convert the arguments from MBCS to UNICODE
92 	*/
93 	int i, result;
94 	wchar_t **newArgv = malloc((argc + 1) * sizeof(wchar_t *));
95 	for (i=0; i<argc; i++) {
96 		char *oldArg = argv[i];
97 		int numChars = MultiByteToWideChar(CP_ACP, 0, oldArg, -1, NULL, 0);
98 		wchar_t *newArg  = malloc((numChars + 1) * sizeof(wchar_t));
99 		newArg[numChars] = 0;
100 		MultiByteToWideChar(CP_ACP, 0, oldArg, -1, newArg, numChars);
101 		newArgv[i] = newArg;
102 	}
103 	newArgv[i] = NULL;
104 	result = mainW(argc, newArgv);
105 	for (i=0; i<argc; i++) {
106 		free(newArgv[i]);
107 	}
108 	free(newArgv);
109 	return result;
110 }
111 
112 #define main mainW
113 #endif /* UNICODE */
114 
main(int argc,_TCHAR * argv[])115 int main( int argc, _TCHAR* argv[] )
116 {
117 	_TCHAR*  errorMsg;
118 	_TCHAR*  program;
119 	_TCHAR*  iniFile;
120 	_TCHAR*  ch;
121 	_TCHAR** configArgv = NULL;
122 	int 	 configArgc = 0;
123 	int      exitCode = 0;
124 	int      ret = 0;
125 	void *	 handle = 0;
126 	RunMethod 		runMethod;
127 	SetInitialArgs  setArgs;
128 
129 	setlocale(LC_ALL, "");
130 
131 	initialArgc = argc;
132 	initialArgv = malloc((argc + 1) * sizeof(_TCHAR*));
133 	memcpy(initialArgv, argv, (argc + 1) * sizeof(_TCHAR*));
134 
135 	/*
136 	 * Strip off any extroneous <CR> from the last argument. If a shell script
137 	 * on Linux is created in DOS format (lines end with <CR><LF>), the C-shell
138 	 * does not strip off the <CR> and hence the argument is bogus and may
139 	 * not be recognized by the launcher or eclipse itself.
140 	 */
141 	 ch = _tcschr( argv[ argc - 1 ], _T_ECLIPSE('\r') );
142 	 if (ch != NULL)
143 	 {
144 	     *ch = _T_ECLIPSE('\0');
145 	 }
146 
147 	 /* Determine the full pathname of this program. */
148 	 program = findProgram(argv);
149 
150     /* Parse configuration file arguments */
151     iniFile = checkForIni(argc, argv);
152     if (iniFile != NULL)
153 		ret = readConfigFile(iniFile, &configArgc, &configArgv);
154     else
155 		ret = readIniFile(program, &configArgc, &configArgv);
156 	if (ret == 0)
157 	{
158 		parseArgs (&configArgc, configArgv, 0);
159 	}
160 
161 	/* Parse command line arguments           */
162     /* Overrides configuration file arguments */
163     parseArgs( &argc, argv, 1);
164 
165     /* Special case - user arguments specified in the config file
166 	 * are appended to the user arguments passed from the command line.
167 	 */
168 	if (configArgc > 0)
169 	{
170 		createUserArgs(configArgc, configArgv, &argc, &argv);
171 	}
172 
173 	/* Initialize official program name */
174 	officialName = name != NULL ? _tcsdup( name ) : getDefaultOfficialName(program);
175 
176 	/* Find the directory where the Eclipse program is installed. */
177     programDir = getDirFromProgram(program);
178 
179 	/* Find the eclipse library */
180     eclipseLibrary = findLibrary(eclipseLibrary, program);
181 
182     /* root check */
183 	if(protectRoot && isRoot()){
184 		errorMsg = malloc( (_tcslen(rootMsg) + _tcslen(officialName) + 10) * sizeof(_TCHAR) );
185 		_stprintf( errorMsg, rootMsg, officialName );
186         if (!suppressErrors)
187         	displayMessage( officialName, errorMsg );
188         else
189         	_ftprintf(stderr, _T_ECLIPSE("%s:\n%s\n"), officialName, errorMsg);
190         free( errorMsg );
191         exit( 2 );
192 	}
193 
194 	if(eclipseLibrary != NULL)
195 		handle = loadLibrary(eclipseLibrary);
196 	if(handle == NULL) {
197 		errorMsg = malloc( (_tcslen(libraryMsg) + _tcslen(officialName) + 10) * sizeof(_TCHAR) );
198         _stprintf( errorMsg, libraryMsg, officialName );
199         if (!suppressErrors)
200         	displayMessage( officialName, errorMsg );
201         else
202         	_ftprintf(stderr, _T_ECLIPSE("%s:\n%s\n"), officialName, errorMsg);
203         free( errorMsg );
204     	exit( 1 );
205 	}
206 
207 	setArgs = (SetInitialArgs)findSymbol(handle, SET_INITIAL_ARGS);
208 	if(setArgs != NULL)
209 		setArgs(initialArgc, initialArgv, eclipseLibrary);
210 	else {
211 		if(!suppressErrors)
212 			displayMessage(officialName, entryMsg);
213 		else
214 			_ftprintf(stderr, _T_ECLIPSE("%s:\n%s\n"), officialName, entryMsg);
215 		exit(1);
216 	}
217 
218 	runMethod = (RunMethod)findSymbol(handle, RUN_METHOD);
219 	if(runMethod != NULL)
220 		exitCode = runMethod(argc, argv, userVMarg);
221 	else {
222 		if(!suppressErrors)
223 			displayMessage(officialName, entryMsg);
224 		else
225 			_ftprintf(stderr, _T_ECLIPSE("%s:\n%s\n"), officialName, entryMsg);
226 		exit(1);
227 	}
228 	unloadLibrary(handle);
229 
230 	free( eclipseLibrary );
231     free( programDir );
232     free( program );
233     free( officialName );
234 
235 	return exitCode;
236 }
237 
getProgramPath()238 _TCHAR* getProgramPath() {
239 	return NULL;
240 }
241 
findProgram(_TCHAR * argv[])242 static _TCHAR* findProgram(_TCHAR* argv[]) {
243 	_TCHAR * program;
244 #ifdef _WIN32
245 	 /* windows, make sure we are looking for the .exe */
246 	_TCHAR * ch;
247 	int length = _tcslen(argv[0]);
248 	ch = malloc( (length + 5) * sizeof(_TCHAR));
249 	_tcscpy(ch, argv[0]);
250 
251 	if (length <= 4 || _tcsicmp( &ch[ length - 4 ], _T_ECLIPSE(".exe") ) != 0)
252 		_tcscat(ch, _T_ECLIPSE(".exe"));
253 
254 	program = findCommand(ch);
255 	if (ch != program)
256 		free(ch);
257 #else
258 	program = findCommand( argv[0] );
259 #endif
260     if (program == NULL)
261     {
262 #ifdef _WIN32
263     	program = malloc( MAX_PATH_LENGTH + 1 );
264     	GetModuleFileName( NULL, program, MAX_PATH_LENGTH );
265     	argv[0] = program;
266 #else
267     	program = malloc( (strlen( argv[0] ) + 1) * sizeof(_TCHAR) );
268     	strcpy( program, argv[0] );
269 #endif
270     } else if (_tcscmp(argv[0], program) != 0) {
271     	argv[0] = program;
272     }
273     return program;
274 }
275 
276 /*
277  * Parse arguments of the command.
278  */
parseArgs(int * pArgc,_TCHAR * argv[],int useVMargs)279 static void parseArgs( int* pArgc, _TCHAR* argv[], int useVMargs )
280 {
281     int     index;
282 
283     /* Ensure the list of user argument is NULL terminated. */
284     argv[ *pArgc ] = NULL;
285 
286 	/* For each user defined argument */
287     for (index = 0; index < *pArgc; index++){
288         if(_tcsicmp(argv[index], VMARGS) == 0) {
289         	if (useVMargs == 1)	{ //Use the VMargs as the user specified vmArgs
290         		userVMarg = &argv[ index+1 ];
291         	}
292             argv[ index ] = NULL;
293             *pArgc = index;
294         } else if(_tcsicmp(argv[index], NAME) == 0) {
295         	name = argv[++index];
296         } else if(_tcsicmp(argv[index], LIBRARY) == 0) {
297         	eclipseLibrary = argv[++index];
298         } else if(_tcsicmp(argv[index], SUPRESSERRORS) == 0) {
299         	suppressErrors = 1;
300         } else if(_tcsicmp(argv[index], PROTECT) == 0) {
301         	if(_tcsicmp(argv[++index], ROOT) == 0){
302         		protectRoot = 1;
303         	}
304         }
305     }
306 }
307 
308 /* We need to look for --launcher.ini before parsing the other args */
checkForIni(int argc,_TCHAR * argv[])309 static _TCHAR* checkForIni(int argc, _TCHAR* argv[])
310 {
311 	int index;
312 	for(index = 0; index < (argc - 1); index++) {
313 		if(_tcsicmp(argv[index], INI) == 0) {
314         	return argv[++index];
315         }
316 	}
317 	return NULL;
318 }
319 
320 /*
321  * Create a new array containing user arguments from the config file first and
322  * from the command line second.
323  * Allocate an array large enough to host all the strings passed in from
324  * the argument configArgv and argv. That array is passed back to the
325  * argv argument. That array must be freed with the regular free().
326  * Note that both arg lists are expected to contain the argument 0 from the C
327  * main method. That argument contains the path/executable name. It is
328  * only copied once in the resulting list.
329  *
330  * Returns 0 if success.
331  */
createUserArgs(int configArgc,_TCHAR ** configArgv,int * argc,_TCHAR *** argv)332 static int createUserArgs(int configArgc, _TCHAR **configArgv, int *argc, _TCHAR ***argv)
333 {
334 	_TCHAR** newArray = (_TCHAR **)malloc((configArgc + *argc + 1) * sizeof(_TCHAR *));
335 
336 	newArray[0] = (*argv)[0];	/* use the original argv[0] */
337 	memcpy(newArray + 1, configArgv, configArgc * sizeof(_TCHAR *));
338 
339 	/* Skip the argument zero (program path and name) */
340 	memcpy(newArray + 1 + configArgc, *argv + 1, (*argc - 1) * sizeof(_TCHAR *));
341 
342 	/* Null terminate the new list of arguments and return it. */
343 	*argv = newArray;
344 	*argc += configArgc;
345 	(*argv)[*argc] = NULL;
346 
347 	return 0;
348 }
349 
350 /* Determine the Program Directory
351  *
352  * This function takes the directory where program executable resides and
353  * determines the installation directory.
354  */
getDirFromProgram(_TCHAR * program)355 _TCHAR* getDirFromProgram(_TCHAR* program)
356 {
357 	_TCHAR*  ch;
358 
359 	if(programDir != NULL)
360 		return programDir;
361 
362     programDir = malloc( (_tcslen( program ) + 1) * sizeof(_TCHAR) );
363     _tcscpy( programDir, program );
364     ch = lastDirSeparator( programDir );
365 	if (ch != NULL)
366     {
367     	*(ch+1) = _T_ECLIPSE('\0');
368    		return programDir;
369     }
370 
371 	/* Can't figure out from the program, lets use the cwd */
372 	free(programDir);
373 	programDir = malloc( MAX_PATH_LENGTH * sizeof (_TCHAR));
374 	_tgetcwd( programDir, MAX_PATH_LENGTH );
375 	return programDir;
376 }
377 
getProgramDir()378 _TCHAR* getProgramDir()
379 {
380 	return programDir;
381 }
382 
getOfficialName()383 _TCHAR* getOfficialName() {
384 	return officialName;
385 }
386 
387 /*
388  * Determine the default official application name
389  *
390  * This function provides the default application name that appears in a variety of
391  * places such as: title of message dialog, title of splash screen window
392  * that shows up in Windows task bar.
393  * It is computed from the name of the launcher executable and
394  * by capitalizing the first letter. e.g. "c:/ide/eclipse.exe" provides
395  * a default name of "Eclipse".
396  */
getDefaultOfficialName(_TCHAR * program)397 static _TCHAR* getDefaultOfficialName(_TCHAR* program)
398 {
399 	_TCHAR *ch = NULL;
400 
401 	/* Skip the directory part */
402 	ch = lastDirSeparator( program );
403 	if (ch == NULL) ch = program;
404 	else ch++;
405 
406 	ch = _tcsdup( ch );
407 #ifdef _WIN32
408 	{
409 		/* Search for the extension .exe and cut it */
410 		_TCHAR *extension = _tcsrchr(ch, _T_ECLIPSE('.'));
411 		if (extension != NULL)
412 		{
413 			*extension = _T_ECLIPSE('\0');
414 		}
415 	}
416 #endif
417 	/* Upper case the first character */
418 #ifndef LINUX
419 	{
420 		*ch = _totupper(*ch);
421 	}
422 #else
423 	{
424 		if (*ch >= 'a' && *ch <= 'z')
425 		{
426 			*ch -= 32;
427 		}
428 	}
429 #endif
430 	return ch;
431 }
432 
findLibrary(_TCHAR * library,_TCHAR * program)433 static _TCHAR* findLibrary(_TCHAR* library, _TCHAR* program)
434 {
435 	_TCHAR* c;
436 	_TCHAR* path;
437 	_TCHAR* fragment;
438 	_TCHAR* result;
439 	_TCHAR* dot = _T_ECLIPSE(".");
440 	size_t progLength, pathLength;
441 	size_t fragmentLength;
442 	struct _stat stats;
443 
444 	if (library != NULL) {
445 		path = checkPath(library, programDir, 1);
446 		if (_tstat(path, &stats) == 0 && (stats.st_mode & S_IFDIR) != 0)
447         {
448             /* directory, find the highest version eclipse_* library */
449             result = findFile(path, _T_ECLIPSE("eclipse"));
450         } else {
451         	/* file, return it */
452         	result = _tcsdup(path);
453         }
454 
455 		if (path != library)
456 			free(path);
457 		return result;
458 	}
459 
460 	/* build the equinox.launcher fragment name */
461 	fragmentLength = _tcslen(DEFAULT_EQUINOX_STARTUP) + 1 + _tcslen(wsArg) + 1 + _tcslen(osArg) + 1 + _tcslen(osArchArg) + 1;
462 	fragment = malloc(fragmentLength * sizeof(_TCHAR));
463 	_tcscpy(fragment, DEFAULT_EQUINOX_STARTUP);
464 	_tcscat(fragment, dot);
465 	_tcscat(fragment, wsArg);
466 	_tcscat(fragment, dot);
467 	_tcscat(fragment, osArg);
468 	//!(fragmentOS.equals(Constants.OS_MACOSX) && !Constants.ARCH_X86_64.equals(fragmentArch))
469 #if !(defined(MACOSX) && !defined(__x86_64__))
470 	/* The Mac fragment covers both archs and does not have that last segment */
471 	_tcscat(fragment, dot);
472 	_tcscat(fragment, osArchArg);
473 #endif
474 	progLength = pathLength = _tcslen(programDir);
475 #ifdef MACOSX
476 	pathLength += 9;
477 #endif
478 	path = malloc( (pathLength + 1 + 7 + 1) * sizeof(_TCHAR));
479 	_tcscpy(path, programDir);
480 	if (!IS_DIR_SEPARATOR(path[progLength - 1])) {
481 		path[progLength] = dirSeparator;
482 		path[progLength + 1] = 0;
483 	}
484 #ifdef MACOSX
485 	_tcscat(path, _T_ECLIPSE("../../../"));
486 #endif
487 	_tcscat(path, _T_ECLIPSE("plugins"));
488 
489 	c = findFile(path, fragment);
490 	free(fragment);
491 	if (c == NULL)
492 		return c;
493 	fragment = c;
494 
495 	result = findFile(fragment, _T_ECLIPSE("eclipse"));
496 
497 	free(fragment);
498 	free(path);
499 
500 	return result;
501 }
502 
isRoot()503 static int isRoot(){
504 #ifdef LINUX
505 	return geteuid() == 0;
506 #endif
507 	return 0;
508 }
509