public NodeConfig(string relativeTo) { RelativeTo = relativeTo; /* * Read wrapper config: wrapper log location, PID file location, anchor location. * The PID file location is specified on the command line, so if none is read * it will use a default. It's not in the default wrapper.conf and is defined on * the command line in run.sh. */ PidFilename = "freenet.pid"; // wrapper.conf is relative to the wrapper's location. var wrapperDir = Directory.GetParent(Path.Combine(relativeTo, WrapperFilename())); foreach (var line in File.ReadAllLines(wrapperDir.FullName + '\\' + WrapperConfFilename)) { // TODO: Map between constants and variables to reduce repetition? if (Defines(line, "wrapper.logfile")) { WrapperLogFilename = Path.Combine(relativeTo, Value(line)); } else if (Defines(line, "wrapper.pidfile")) { PidFilename = Path.Combine(relativeTo, Value(line)); } else if (Defines(line, "wrapper.anchorfile")) { AnchorFilename = Path.Combine(relativeTo, Value(line)); } } // TODO: A mapping between config location and variable would reduce verbosity here too. if (WrapperLogFilename == null) { throw new MissingConfigValueException(WrapperConfFilename, "wrapper.logfile"); } if (AnchorFilename == null) { throw new MissingConfigValueException(WrapperConfFilename, "wrapper.anchorfile"); } // Read Freenet config: FProxy port TODO: Use ini-parser instead // TODO: Does this need to wait until the node is running for the first run? var freenetIniLines = File.ReadAllLines(Path.Combine(relativeTo, FreenetIniFilename)); var port = RequireValue(freenetIniLines, "fproxy.port"); var isValid = int.TryParse(port, out FProxyPort); if (!isValid) { FNLog.Error("fproxy.port is not an integer."); throw new MissingConfigValueException(FreenetIniFilename, "fproxy.port"); } DownloadsDir = Path.Combine(RelativeTo, RequireValue(freenetIniLines, "node.downloadsDir")); }
static void Main() { // migrate settings from older config files for previous assembly versions FreenetTray.Properties.Settings.Default.Upgrade(); FNLog.Initialize(); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new CommandsMenu()); }
private void Wrapper_Exited(object sender, EventArgs e) { // TODO: Is exit code enough to distinguish between stopping and crashing? if (_wrapper.ExitCode == 0) { FNLog.Debug("Wrapper exited."); OnStopped(sender, e); } else { FNLog.Error("Wrapper crashed. Exit code: {0}", _wrapper.ExitCode); OnCrashed(CrashType.WrapperCrashed); } }
/* * TODO: What are the function documentation comments supposed to be formatted like? * Start the node if it is not already started. * * Throws FileNotFoundException */ public void Start() { if (IsRunning()) { return; } try { // TODO: Under what circumstances will Process.Start() return null? _wrapper = Process.Start(_wrapperInfo); _wrapper.EnableRaisingEvents = true; _wrapper.Exited += Wrapper_Exited; } catch (Win32Exception ex) { // http://msdn.microsoft.com/en-us/library/0w4h05yb%28v=vs.110%29.aspx switch (ex.NativeErrorCode) { case ERROR_FILE_NOT_FOUND: FNLog.Error("Cannot start Freenet: wrapper executable not found."); OnCrashed(CrashType.WrapperFileNotFound); return; case ERROR_INSUFFICIENT_BUFFER: case ERROR_ACCESS_DENIED: FNLog.Error("Cannot start Freenet: the file path is too long."); OnCrashed(CrashType.PathTooLong); return; default: FNLog.ErrorException(ex, "Cannot start Freenet: Process.Start() gave an error code it is not documented as giving."); throw; } } OnStarted(this, null); }
// TODO: Where to document? Throws FileNotFound, DirectoryNotFound, MissingJRE public NodeController() { if (Properties.Settings.Default.CustomLocation.Length != 0) { _config = new NodeConfig(Properties.Settings.Default.CustomLocation); } else { Exception configException = null; foreach (var path in new[] { Directory.GetCurrentDirectory(), Environment.ExpandEnvironmentVariables(@"%LocalAppData%\Freenet"), }) { // TODO: If the wrapper has problems with arguments with non-ASCII characters should // this the wrapper invocation change the working directory? Won't work in the general // case because the pidfile location could contain non-ASCII characters, but it // works for the default configuration. // http://sourceforge.net/p/wrapper/bugs/290/ try { _config = new NodeConfig(path); configException = null; break; } catch (Exception e) { configException = e; } } if (configException != null) { FNLog.Error("Failed to detect Freenet installation.", configException); throw configException; } } // Search for an existing wrapper process. try { using (var reader = new StreamReader(_config.PidFilename)) { var line = reader.ReadLine(); if (line != null) { var pid = int.Parse(line); _wrapper = Process.GetProcessById(pid); _wrapper.EnableRaisingEvents = true; _wrapper.Exited += Wrapper_Exited; } } } catch (ArgumentException) { FNLog.Debug("No process has the PID in the PID file."); // The wrapper can refuse to start if there is a stale PID file - "strict". try { File.Delete(_config.PidFilename); } catch (IOException) { // TODO: Be louder about this? Or will the wrapper fail to start and exit nonzero? FNLog.Debug("Stale PID file is still held."); } } catch (FormatException) { FNLog.Debug("PID file does not contain an integer."); } catch (OverflowException) { FNLog.Debug("PID file does not contain an integer."); } catch (FileNotFoundException) { FNLog.Debug("PID file not found."); } /* * Hide the wrapper window when launching it. This prevents (or at least heavily complicates) * stopping it with Process.CloseMainWindow() or by sending ctrl + C. */ _wrapperInfo.FileName = Path.Combine(_config.RelativeTo, WrapperFilename()); // TODO: Is it worthwhile to omit the pidfile here when it's in the config file? _wrapperInfo.Arguments = "-c " + WrapperConfFilename + " wrapper.pidfile=" + _config.PidFilename; _wrapperInfo.UseShellExecute = false; _wrapperInfo.CreateNoWindow = true; }
private void FindNode() { while (true) { try { _node = new NodeController(); break; } catch (FileNotFoundException e) { FNLog.ErrorException(e, "Failed to find file"); } catch (DirectoryNotFoundException e) { FNLog.ErrorException(e, "Failed to find directory"); } catch (NodeController.MissingConfigValueException e) { // If the configuration files exist but are missing required // values it is sufficiently surprising to warrant an error // dialog. FNLog.Error(strings.MalformedConfig, e.Filename, e.Value); MessageBox.Show(String.Format(strings.MalformedConfig, e.Filename, e.Value), "", MessageBoxButtons.OK, MessageBoxIcon.Error); } catch (MissingJRE) { // No JRE was found FNLog.Error(strings.JRENotFound); MessageBox.Show(strings.JRENotFound, "", MessageBoxButtons.OK, MessageBoxIcon.Error); /* Cannot continue for now */ Application.Exit(); return; } // TODO: Explain what happened to prompt a custom location? try { PreferencesWindow.PromptCustomLocation(this); } catch (OperationCanceledException) { /* User exited the file browser. */ Application.Exit(); return; } } _node.OnStarted += NodeStarted; _node.OnStopped += NodeStopped; _node.OnCrashed += NodeCrashed; foreach (var menuItem in new[] { openFreenetMenuItem, startFreenetMenuItem, stopFreenetMenuItem, downloadsMenuItem, viewLogsMenuItem, preferencesMenuItem, hideIconMenuItem, }) { menuItem.Enabled = true; } // Set menu up for whether there is an existing node. RefreshMenu(_node.IsRunning()); ReadCommandLine(); }
private void openFreenetMenuItem_Click(object sender = null, EventArgs e = null) { Start(); var pollFproxy = new Thread(() => { var fproxyListening = false; var showSlowOpen = Settings.Default.ShowSlowOpenTip; var openArgs = e as OpenArgs; if (openArgs != null) { showSlowOpen = openArgs.ShowSlow; } /* * TODO: Programatic way to get loopback address? This would not support IPv6. * Use FProxy bind interface? */ var loopback = new IPAddress(new byte[] { 127, 0, 0, 1 }); var timer = new Stopwatch(); using (var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) { timer.Start(); while (_node.IsRunning()) { try { sock.Connect(loopback, _node.FProxyPort); fproxyListening = true; break; } catch (SocketException ex) { FNLog.Debug("Connecting got error: {0}", Enum.GetName(typeof(SocketError), ex.SocketErrorCode)); Thread.Sleep(SocketPollInterval); } // Show a startup notification if it's taking a while. if (showSlowOpen && timer.ElapsedMilliseconds > SlowOpenThreshold) { BeginInvoke(new Action(() => { trayIcon.BalloonTipText = strings.FreenetStarting; trayIcon.ShowBalloonTip(SlowOpenTimeout); })); showSlowOpen = false; } } timer.Stop(); } if (fproxyListening) { FNLog.Debug("FProxy listening after {0}", timer.Elapsed); BrowserUtil.Open(new Uri(String.Format("http://localhost:{0:d}", _node.FProxyPort)), true); } }); pollFproxy.Start(); }