1 /*
2  * Copyright (C) Azureus Software, Inc, All Rights Reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details ( see the LICENSE file ).
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18 
19 package org.gudy.azureus2.platform.unix;
20 
21 import java.io.*;
22 import java.lang.reflect.Constructor;
23 import java.lang.reflect.Method;
24 import java.util.ArrayList;
25 import java.util.Iterator;
26 import java.util.regex.Matcher;
27 import java.util.regex.Pattern;
28 
29 import org.gudy.azureus2.core3.config.COConfigurationManager;
30 import org.gudy.azureus2.core3.util.Constants;
31 import org.gudy.azureus2.core3.util.Debug;
32 import org.gudy.azureus2.core3.util.FileUtil;
33 
34 import com.aelitis.azureus.core.impl.AzureusCoreSingleInstanceClient;
35 
36 import org.gudy.azureus2.plugins.PluginManager;
37 
38 public class ScriptBeforeStartup
39 {
40 	private static PrintStream sysout;
41 
42 	private static Object display;
43 
main(String[] args)44 	public static void main(String[] args) {
45 		// Set transitory so not everything gets loaded up. (such as the AEDiagnostic's tidy flag)
46 		System.setProperty("transitory.startup", "1");
47 
48 		// Since stdout will be in a shell script, redirect any stdout not coming
49 		// from us to stderr
50 		sysout = System.out;
51 		try {
52 			System.setOut(new PrintStream(new FileOutputStream("/dev/stderr")));
53 		} catch (FileNotFoundException e) {
54 		}
55 
56   	String mi_str = System.getProperty(PluginManager.PR_MULTI_INSTANCE);
57 		boolean mi = mi_str != null && mi_str.equalsIgnoreCase("true");
58 
59 		if (!mi) {
60   		boolean argsSent = new AzureusCoreSingleInstanceClient().sendArgs(args, 500);
61   		if (argsSent) {
62   			// azureus was open..
63   			String msg = "Passing startup args to already-running " + Constants.APP_NAME + " java process listening on [127.0.0.1: " + Constants.INSTANCE_PORT + "]";
64   			log(msg);
65   			sysout.println("exit");
66 
67   			return;
68   		}
69 		}
70 
71 		// If the after shutdown script didn't run or crapped out, then
72 		// don't run again..
73 		String scriptAfterShutdown = COConfigurationManager.getStringParameter(
74 				"scriptaftershutdown", null);
75 
76 		COConfigurationManager.removeParameter("scriptaftershutdown.exit");
77 		COConfigurationManager.removeParameter("scriptaftershutdown");
78 		COConfigurationManager.save();
79 		if (scriptAfterShutdown != null) {
80 			log("Script after " + Constants.APP_NAME + " shutdown did not run.. running now");
81 
82 			sysout.println(scriptAfterShutdown);
83 
84 			if (scriptAfterShutdown.indexOf("$0") < 0) {
85 				// doesn't have a restart.. add one
86 				sysout.println("echo \"Restarting Azureus..\"");
87 				sysout.println("$0\n");
88 			}
89 			// exit is a requirement
90 			sysout.println("exit");
91 
92 			return;
93 		}
94 
95 		String moz = getNewGreDir();
96 
97 		if (moz != null) {
98 			String s = "export MOZILLA_FIVE_HOME=\"" + moz + "\"\n"
99 					+ "if [ \"$LD_LIBRARY_PATH x\" = \" x\" ] ; then\n"
100 					+ "	export LD_LIBRARY_PATH=$MOZILLA_FIVE_HOME;\n" + "else\n"
101 					+ "	export LD_LIBRARY_PATH=$MOZILLA_FIVE_HOME:$LD_LIBRARY_PATH\n"
102 					+ "fi\n";
103 			sysout.println(s);
104 			log("setting LD_LIBRARY_PATH to: $LD_LIBRARY_PATH");
105 			log("setting MOZILLA_FIVE_HOME to: $MOZILLA_FIVE_HOME");
106 		} else {
107 			log("Usable browser found");
108 		}
109 	}
110 
getNewGreDir()111 	public static String getNewGreDir() {
112 		// SWT does a pretty awesome job at finding GRE, most cases this will work
113 		if (canOpenBrowser()) {
114 			return null;
115 		}
116 
117 		// TODO: Store last successful dir somewhere and check that first
118 		//       COConfigurationManager probably a bad idea, since that may load
119 		//       Logger and who knows what other libraries
120 		String grePath = null;
121 		final String[] confList = {
122 			"/etc/gre64.conf",
123 			"/etc/gre.d/gre64.conf",
124 			"/etc/gre.conf",
125 			"/etc/gre.d/gre.conf",
126 			"/etc/gre.d/xulrunner.conf",
127 			"/etc/gre.d/libxul0d.conf"
128 		};
129 
130 		log("Auto-scanning for GRE/XULRunner.  You can skip this by appending the GRE path to LD_LIBRARY_PATH and setting MOZILLA_FIVE_HOME.");
131 		try {
132 			Pattern pat = Pattern.compile("GRE_PATH=(.*)", Pattern.CASE_INSENSITIVE);
133 			for (int i = 0; i < confList.length; i++) {
134 				File file = new File(confList[i]);
135 				if (file.isFile() && file.canRead()) {
136 					log("  checking " + file + " for GRE_PATH");
137 					String fileText = FileUtil.readFileAsString(file, 16384);
138 					if (fileText != null) {
139 						Matcher matcher = pat.matcher(fileText);
140 						if (matcher.find()) {
141 							String possibleGrePath = matcher.group(1);
142 							if (isValidGrePath(new File(possibleGrePath))) {
143 								grePath = possibleGrePath;
144 								break;
145 							}
146 						}
147 					}
148 				}
149 			}
150 
151 			if (grePath == null) {
152 				final ArrayList possibleDirs = new ArrayList();
153 				File libDir = new File("/usr");
154 				libDir.listFiles(new FileFilter() {
155 					public boolean accept(File pathname) {
156 						if (pathname.getName().startsWith("lib")) {
157 							possibleDirs.add(pathname);
158 						}
159 						return false;
160 					}
161 				});
162 				possibleDirs.add(new File("/usr/local"));
163 				possibleDirs.add(new File("/opt"));
164 
165 				final String[] possibleDirNames = {
166 					"mozilla",
167 					"firefox",
168 					"seamonkey",
169 					"xulrunner",
170 				};
171 
172 				FileFilter ffIsPossibleDir = new FileFilter() {
173 					public boolean accept(File pathname) {
174 						String name = pathname.getName().toLowerCase();
175 						for (int i = 0; i < possibleDirNames.length; i++) {
176 							if (name.startsWith(possibleDirNames[i])) {
177 								return true;
178 							}
179 						}
180 						return false;
181 					}
182 				};
183 
184 				for (Iterator iter = possibleDirs.iterator(); iter.hasNext();) {
185 					File dir = (File) iter.next();
186 
187 					File[] possibleFullDirs = dir.listFiles(ffIsPossibleDir);
188 
189 					for (int i = 0; i < possibleFullDirs.length; i++) {
190 						log("  checking " + possibleFullDirs[i] + " for GRE");
191 						if (isValidGrePath(possibleFullDirs[i])) {
192 							grePath = possibleFullDirs[i].getAbsolutePath();
193 							break;
194 						}
195 					}
196 					if (grePath != null) {
197 						break;
198 					}
199 				}
200 			}
201 
202 			if (grePath != null) {
203 				log("GRE found at " + grePath + ".");
204 				System.setProperty("org.eclipse.swt.browser.XULRunnerPath", grePath);
205 			}
206 		} catch (Throwable t) {
207 			log("Error trying to find suitable GRE: "
208 					+ Debug.getNestedExceptionMessage(t));
209 			grePath = null;
210 		}
211 
212 		if (!canOpenBrowser()) {
213 			log("Can't create browser.  Will try to set LD_LIBRARY_PATH and hope "
214 					+ Constants.APP_NAME + " has better luck.");
215 		}
216 
217 		return grePath;
218 	}
219 
canOpenBrowser()220 	private static boolean canOpenBrowser() {
221 		try {
222 			Class claDisplay = Class.forName("org.eclipse.swt.widgets.Display");
223 			if (display != null) {
224 				display = claDisplay.newInstance();
225 			}
226 			Class claShell = Class.forName("org.eclipse.swt.widgets.Shell");
227 			Constructor shellConstruct = claShell.getConstructor(new Class[] {
228 				claDisplay,
229 			});
230 			Object shell = shellConstruct.newInstance(new Object[] {
231 				display
232 			});
233 
234 			Class claBrowser = Class.forName("org.eclipse.swt.browser.Browser");
235 			Constructor[] constructors = claBrowser.getConstructors();
236 			for (int i = 0; i < constructors.length; i++) {
237 				if (constructors[i].getParameterTypes().length == 2) {
238 					Object browser = constructors[i].newInstance(new Object[] {
239 						shell,
240 						new Integer(0)
241 					});
242 
243 					Method methSetUrl = claBrowser.getMethod("setUrl", new Class[] {
244 						String.class
245 					});
246 					methSetUrl.invoke(browser, new Object[] {
247 						"about:blank"
248 					});
249 
250 					break;
251 				}
252 			}
253 			Method methDisposeShell = claShell.getMethod("dispose", new Class[] {});
254 			methDisposeShell.invoke(shell, new Object[] {});
255 
256 			return true;
257 		} catch (Throwable e) {
258 			log("Browser check failed with: " + Debug.getNestedExceptionMessage(e));
259 			return false;
260 		}
261 
262 	}
263 
isValidGrePath(File dir)264 	private static boolean isValidGrePath(File dir) {
265 		if (!dir.isDirectory()) {
266 			return false;
267 		}
268 
269 		if (new File(dir, "components/libwidget_gtk.so").exists()
270 				|| new File(dir, "libwidget_gtk.so").exists()) {
271 			log("	Can not use GRE from " + dir
272 					+ " as it's too old (GTK2 version required).");
273 			return false;
274 		}
275 
276 		// newer GRE doesn't have libwidget at all, but older ones do, and it's
277 		// gtk2, we are good to go
278 		if (new File(dir, "components/libwidget_gtk2.so").exists()
279 				|| new File(dir, "libwidget_gtk2.so").exists()) {
280 			return true;
281 		}
282 
283 		if (!new File(dir, "components/libxpcom.so").exists()
284 				&& !new File(dir, "libxpcom.so").exists()) {
285 			log("	Can not use GRE from " + dir + " because it's missing libxpcom.so.");
286 			return false;
287 		}
288 
289 		return true;
290 	}
291 
log(String string)292 	private static void log(String string) {
293 		sysout.println("echo \"" + string.replaceAll("\"", "\\\"") + "\"");
294 	}
295 }
296