This is an automated email from the git hooks/post-receive script.
richard pushed a commit to branch tor-browser-102.3.0esr-12.0-2 in repository tor-browser.
commit 1a64d858c5ab472eec6fc48a43b9ff54942971a5 Author: Pier Angelo Vendrame pierov@torproject.org AuthorDate: Mon Oct 10 15:26:50 2022 +0200
fixup! Bug 40933: Add tor-launcher functionality
Bug 40853: Use Subprocess.jsm to launch tor. --- .../components/tor-launcher/TorMonitorService.jsm | 16 +- toolkit/components/tor-launcher/TorProcess.jsm | 205 +++++++++++---------- 2 files changed, 110 insertions(+), 111 deletions(-)
diff --git a/toolkit/components/tor-launcher/TorMonitorService.jsm b/toolkit/components/tor-launcher/TorMonitorService.jsm index 16d7fc927adc..201ac6275c56 100644 --- a/toolkit/components/tor-launcher/TorMonitorService.jsm +++ b/toolkit/components/tor-launcher/TorMonitorService.jsm @@ -103,6 +103,10 @@ const TorMonitorService = { // control connection is closed. Therefore, as a matter of facts, calling this // function also makes the child Tor instance stop. uninit() { + if (this._torProcess) { + this._torProcess.forget(); + this._torProcess = null; + } this._shutDownEventMonitor(); },
@@ -179,12 +183,11 @@ const TorMonitorService = {
async _startProcess() { this._torProcess = new TorProcess(); - this._torProcess.onExit = unexpected => { - this._shutDownEventMonitor(!unexpected); + this._torProcess.onExit = () => { Services.obs.notifyObservers(null, TorTopics.ProcessExited); }; this._torProcess.onRestart = async () => { - this._shutDownEventMonitor(false); + this._shutDownEventMonitor(); await this._controlTor(); Services.obs.notifyObservers(null, TorTopics.ProcessRestarted); }; @@ -434,13 +437,8 @@ const TorMonitorService = { } },
- _shutDownEventMonitor(shouldCallStop = true) { + _shutDownEventMonitor() { if (this._connection) { - if (this.ownsTorDaemon && this._torProcess && shouldCallStop) { - this._torProcess.stop(); - this._torProcess = null; - } - this._connection.close(); this._connection = null; this._eventMonitorInProgressReply = null; diff --git a/toolkit/components/tor-launcher/TorProcess.jsm b/toolkit/components/tor-launcher/TorProcess.jsm index 78bf2504c9b6..a23fd324efff 100644 --- a/toolkit/components/tor-launcher/TorProcess.jsm +++ b/toolkit/components/tor-launcher/TorProcess.jsm @@ -8,6 +8,10 @@ const { XPCOMUtils } = ChromeUtils.import( "resource://gre/modules/XPCOMUtils.jsm" );
+const { Subprocess } = ChromeUtils.import( + "resource://gre/modules/Subprocess.jsm" +); + ChromeUtils.defineModuleGetter( this, "TorProtocolService", @@ -32,11 +36,6 @@ const TorProcessTopics = Object.freeze({ ProcessDidNotStart: "TorProcessDidNotStart", });
-const ProcessTopics = Object.freeze({ - ProcessFailed: "process-failed", - ProcessFinished: "process-finished", -}); - // Logger adapted from CustomizableUI.jsm XPCOMUtils.defineLazyGetter(this, "logger", () => { const { ConsoleAPI } = ChromeUtils.import( @@ -73,15 +72,6 @@ class TorProcess { ); }
- observe(aSubject, aTopic, aParam) { - if ( - ProcessTopics.ProcessFailed === aTopic || - ProcessTopics.ProcessFinished === aTopic - ) { - this._processExited(); - } - } - async start() { if (this._torProcess) { return; @@ -112,31 +102,27 @@ class TorProcess { // Set an environment variable that points to the Tor data directory. // This is used by meek-client-torbrowser to find the location for // the meek browser profile. - const env = Cc["@mozilla.org/process/environment;1"].getService( - Ci.nsIEnvironment - ); - env.set("TOR_BROWSER_TOR_DATA_DIR", this._dataDir.path); + const environment = { + TOR_BROWSER_TOR_DATA_DIR: this._dataDir.path, + };
// On Windows, prepend the Tor program directory to PATH. This is needed // so that pluggable transports can find OpenSSL DLLs, etc. // See https://trac.torproject.org/projects/tor/ticket/10845 if (TorLauncherUtil.isWindows) { let path = this._exeFile.parent.path; + const env = Cc["@mozilla.org/process/environment;1"].getService( + Ci.nsIEnvironment + ); if (env.exists("PATH")) { path += ";" + env.get("PATH"); } - env.set("PATH", path); + environment.PATH = path; }
this._status = TorProcessStatus.Starting; this._didConnectToTorControlPort = false;
- var p = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess); - p.startHidden = true; - p.init(this._exeFile); - - logger.debug(`Starting ${this._exeFile.path}`, this._args); - // useful for simulating slow tor daemon launch const kPrefTorDaemonLaunchDelay = "extensions.torlauncher.launch_delay"; const launchDelay = Services.prefs.getIntPref( @@ -146,46 +132,48 @@ class TorProcess { if (launchDelay > 0) { await new Promise(resolve => setTimeout(() => resolve(), launchDelay)); } - p.runwAsync(this._args, this._args.length, this, false); - // FIXME: This should be okay, unless the observer is called just before - // assigning the correct state. - // We should check for the control connection status, rather than the - // process status. - if (this._status === TorProcessStatus.Starting) { - this._status = TorProcessStatus.Running; - }
- this._torProcess = p; + logger.debug(`Starting ${this._exeFile.path}`, this._args); + const options = { + command: this._exeFile.path, + arguments: this._args, + environment, + environmentAppend: true, + stderr: "pipe", + }; + this._torProcess = await Subprocess.call(options); + this._watchProcess(); + this._status = TorProcessStatus.Running; this._torProcessStartTime = Date.now(); } catch (e) { this._status = TorProcessStatus.Exited; - const s = TorLauncherUtil.getLocalizedString("tor_failed_to_start"); - TorLauncherUtil.notifyUserOfError( - s, + this._torProcess = null; + logger.error("startTor error:", e); + Services.obs.notifyObservers( null, - TorProcessTopics.ProcessDidNotStart + TorProcessTopics.ProcessDidNotStart, + null ); - logger.error("startTor error:", e); } }
- stop() { - if (this._torProcess) { - // We now rely on the TAKEOWNERSHIP feature to shut down tor when we - // close the control port connection. - // - // Previously, we sent a SIGNAL HALT command to the tor control port, - // but that caused hangs upon exit in the Firefox 24.x based browser. - // Apparently, Firefox does not like to process socket I/O while - // quitting if the browser did not finish starting up (e.g., when - // someone presses the Quit button on our Network Settings window - // during startup). - // - // However, we set the Subprocess object to null to distinguish the cases - // in which we wanted to explicitly kill the tor process, from the cases - // in which it exited for any other reason, e.g., a crash. - this._torProcess = null; - } + // Forget about a process. + // + // Instead of killing the tor process, we rely on the TAKEOWNERSHIP feature + // to shut down tor when we close the control port connection. + // + // Previously, we sent a SIGNAL HALT command to the tor control port, + // but that caused hangs upon exit in the Firefox 24.x based browser. + // Apparently, Firefox does not like to process socket I/O while + // quitting if the browser did not finish starting up (e.g., when + // someone presses the Quit button on our Network Settings window + // during startup). + // + // Still, before closing the owning connection, this class should forget about + // the process, so that future notifications will be ignored. + forget() { + this._torProcess = null; + this._status = TorProcessStatus.Exited; }
// The owner of the process can use this function to tell us that they @@ -195,58 +183,71 @@ class TorProcess { this._didConnectToTorControlPort = true; }
- _processExited() { - // When we stop tor intentionally, we also set _torProcess to null. - // So, if this._torProcess is not null, it exited for some other reason. - const unexpected = !!this._torProcess; - if (unexpected) { - logger.warn("The tor process exited unexpectedly."); - } else { - logger.info("The tor process exited."); + async _watchProcess() { + const watched = this._torProcess; + if (!watched) { + return; } + try { + const { exitCode } = await watched.wait();
- this._torProcess = null; - this._status = TorProcessStatus.Exited; - - let restart = false; - if (unexpected) { - // TODO: Move this logic somewhere else? - let s; - if (!this._didConnectToTorControlPort) { - // tor might be misconfigured, becauser we could never connect to it - const key = "tor_exited_during_startup"; - s = TorLauncherUtil.getLocalizedString(key); + if (watched !== this._torProcess) { + logger.debug(`A Tor process exited with code ${exitCode}.`); + } else if (exitCode) { + logger.warn(`The watched Tor process exited with code ${exitCode}.`); } else { - // tor exited suddenly, so configuration should be okay - s = - TorLauncherUtil.getLocalizedString("tor_exited") + - "\n\n" + - TorLauncherUtil.getLocalizedString("tor_exited2"); + logger.info("The Tor process exited."); } - logger.info(s); - var defaultBtnLabel = TorLauncherUtil.getLocalizedString("restart_tor"); - var cancelBtnLabel = "OK"; - try { - const kSysBundleURI = "chrome://global/locale/commonDialogs.properties"; - var sysBundle = Services.strings.createBundle(kSysBundleURI); - cancelBtnLabel = sysBundle.GetStringFromName(cancelBtnLabel); - } catch (e) {} + } catch (e) { + logger.error("Failed to watch the tor process", e); + }
- restart = TorLauncherUtil.showConfirm( - null, - s, - defaultBtnLabel, - cancelBtnLabel - ); - if (restart) { - this.start().then(() => { - if (this.onRestart) { - this.onRestart(); - } - }); - } + if (watched === this._torProcess) { + this._processExitedUnexpectedly(); } - if (!restart && this.onExit) { + } + + _processExitedUnexpectedly() { + this._torProcess = null; + this._status = TorProcessStatus.Exited; + + // TODO: Move this logic somewhere else? + let s; + if (!this._didConnectToTorControlPort) { + // tor might be misconfigured, becauser we could never connect to it + const key = "tor_exited_during_startup"; + s = TorLauncherUtil.getLocalizedString(key); + } else { + // tor exited suddenly, so configuration should be okay + s = + TorLauncherUtil.getLocalizedString("tor_exited") + + "\n\n" + + TorLauncherUtil.getLocalizedString("tor_exited2"); + } + logger.info(s); + const defaultBtnLabel = TorLauncherUtil.getLocalizedString("restart_tor"); + let cancelBtnLabel = "OK"; + try { + const kSysBundleURI = "chrome://global/locale/commonDialogs.properties"; + const sysBundle = Services.strings.createBundle(kSysBundleURI); + cancelBtnLabel = sysBundle.GetStringFromName(cancelBtnLabel); + } catch (e) { + logger.warn("Could not localize the cancel button", e); + } + + const restart = TorLauncherUtil.showConfirm( + null, + s, + defaultBtnLabel, + cancelBtnLabel + ); + if (restart) { + this.start().then(() => { + if (this.onRestart) { + this.onRestart(); + } + }); + } else if (this.onExit) { this.onExit(unexpected); } }