We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Graphical updater "is already running" dialog during late stages of the update process
## Description

The graphical updater briefly shows an "is already running" dialog during the last stages of the process. The dialog can be dismissed and has no negative consequences.

Tested on Tails 3.16.

## Steps to Reproduce

Perform a full graphical update using the graphical updater, with the Tails admin password set.

## Expected Behavior

The graphical update completes without any unusual messages.

## Actual Behavior

An "already running" dialog briefly appears towards the end of the process.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `journalist_gui/journalist_gui/SecureDropUpdater.py`
Content:
```
1 #!/usr/bin/python
2 from PyQt5 import QtGui, QtWidgets
3 from PyQt5.QtCore import QThread, pyqtSignal
4 import subprocess
5 import os
6 import re
7 import pexpect
8 import socket
9 import sys
10 
11 from journalist_gui import updaterUI, strings, resources_rc  # noqa
12 
13 
14 FLAG_LOCATION = "/home/amnesia/Persistent/.securedrop/securedrop_update.flag"  # noqa
15 ESCAPE_POD = re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]')
16 
17 
18 def password_is_set():
19 
20     pwd_flag = subprocess.check_output(['passwd', '--status']).decode('utf-8').split()[1]
21 
22     if pwd_flag == 'NP':
23         return False
24     return True
25 
26 
27 def prevent_second_instance(app: QtWidgets.QApplication, name: str) -> None:  # noqa
28 
29     # Null byte triggers abstract namespace
30     IDENTIFIER = '\0' + name
31     ALREADY_BOUND_ERRNO = 98
32 
33     app.instance_binding = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
34     try:
35         app.instance_binding.bind(IDENTIFIER)
36     except OSError as e:
37         if e.errno == ALREADY_BOUND_ERRNO:
38             err_dialog = QtWidgets.QMessageBox()
39             err_dialog.setText(name + strings.app_is_already_running)
40             err_dialog.exec()
41             sys.exit()
42         else:
43             raise
44 
45 
46 class SetupThread(QThread):
47     signal = pyqtSignal('PyQt_PyObject')
48 
49     def __init__(self):
50         QThread.__init__(self)
51         self.output = ""
52         self.update_success = False
53         self.failure_reason = ""
54 
55     def run(self):
56         sdadmin_path = '/home/amnesia/Persistent/securedrop/securedrop-admin'
57         update_command = [sdadmin_path, 'setup']
58 
59         # Create flag file to indicate we should resume failed updates on
60         # reboot. Don't create the flag if it already exists.
61         if not os.path.exists(FLAG_LOCATION):
62             open(FLAG_LOCATION, 'a').close()
63 
64         try:
65             self.output = subprocess.check_output(
66                 update_command,
67                 stderr=subprocess.STDOUT).decode('utf-8')
68             if 'Failed to install' in self.output:
69                 self.update_success = False
70                 self.failure_reason = strings.update_failed_generic_reason
71             else:
72                 self.update_success = True
73         except subprocess.CalledProcessError as e:
74             self.output += e.output.decode('utf-8')
75             self.update_success = False
76             self.failure_reason = strings.update_failed_generic_reason
77         result = {'status': self.update_success,
78                   'output': self.output,
79                   'failure_reason': self.failure_reason}
80         self.signal.emit(result)
81 
82 
83 # This thread will handle the ./securedrop-admin update command
84 class UpdateThread(QThread):
85     signal = pyqtSignal('PyQt_PyObject')
86 
87     def __init__(self):
88         QThread.__init__(self)
89         self.output = ""
90         self.update_success = False
91         self.failure_reason = ""
92 
93     def run(self):
94         sdadmin_path = '/home/amnesia/Persistent/securedrop/securedrop-admin'
95         update_command = [sdadmin_path, 'update']
96         try:
97             self.output = subprocess.check_output(
98                 update_command,
99                 stderr=subprocess.STDOUT).decode('utf-8')
100             if "Signature verification successful" in self.output:
101                 self.update_success = True
102             else:
103                 self.failure_reason = strings.update_failed_generic_reason
104         except subprocess.CalledProcessError as e:
105             self.update_success = False
106             self.output += e.output.decode('utf-8')
107             if 'Signature verification failed' in self.output:
108                 self.failure_reason = strings.update_failed_sig_failure
109             else:
110                 self.failure_reason = strings.update_failed_generic_reason
111         result = {'status': self.update_success,
112                   'output': self.output,
113                   'failure_reason': self.failure_reason}
114         self.signal.emit(result)
115 
116 
117 # This thread will handle the ./securedrop-admin tailsconfig command
118 class TailsconfigThread(QThread):
119     signal = pyqtSignal('PyQt_PyObject')
120 
121     def __init__(self):
122         QThread.__init__(self)
123         self.output = ""
124         self.update_success = False
125         self.failure_reason = ""
126         self.sudo_password = ""
127 
128     def run(self):
129         tailsconfig_command = ("/home/amnesia/Persistent/"
130                                "securedrop/securedrop-admin "
131                                "tailsconfig")
132         try:
133             child = pexpect.spawn(tailsconfig_command)
134             child.expect('SUDO password:')
135             self.output += child.before.decode('utf-8')
136             child.sendline(self.sudo_password)
137             child.expect(pexpect.EOF)
138             self.output += child.before.decode('utf-8')
139             child.close()
140 
141             # For Tailsconfig to be considered a success, we expect no
142             # failures in the Ansible output.
143             if child.exitstatus:
144                 self.update_success = False
145                 self.failure_reason = strings.tailsconfig_failed_generic_reason  # noqa
146             else:
147                 self.update_success = True
148         except pexpect.exceptions.TIMEOUT:
149             self.update_success = False
150             self.failure_reason = strings.tailsconfig_failed_sudo_password
151 
152         except subprocess.CalledProcessError:
153             self.update_success = False
154             self.failure_reason = strings.tailsconfig_failed_generic_reason
155         result = {'status': self.update_success,
156                   'output': ESCAPE_POD.sub('', self.output),
157                   'failure_reason': self.failure_reason}
158         self.signal.emit(result)
159 
160 
161 class UpdaterApp(QtWidgets.QMainWindow, updaterUI.Ui_MainWindow):
162 
163     def __init__(self, parent=None):
164         super(UpdaterApp, self).__init__(parent)
165         self.setupUi(self)
166         self.statusbar.setSizeGripEnabled(False)
167         self.output = strings.initial_text_box
168         self.plainTextEdit.setPlainText(self.output)
169         self.update_success = False
170 
171         pixmap = QtGui.QPixmap(":/images/static/banner.png")
172         self.label_2.setPixmap(pixmap)
173         self.label_2.setScaledContents(True)
174 
175         self.progressBar.setProperty("value", 0)
176         self.setWindowTitle(strings.window_title)
177         self.setWindowIcon(QtGui.QIcon(':/images/static/securedrop_icon.png'))
178         self.label.setText(strings.update_in_progress)
179 
180         self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab),
181                                   strings.main_tab)
182         self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2),
183                                   strings.output_tab)
184 
185         # Connect buttons to their functions.
186         self.pushButton.setText(strings.install_later_button)
187         self.pushButton.setStyleSheet("""background-color: lightgrey;
188                                       min-height: 2em;
189                                       border-radius: 10px""")
190         self.pushButton.clicked.connect(self.close)
191         self.pushButton_2.setText(strings.install_update_button)
192         self.pushButton_2.setStyleSheet("""background-color: #E6FFEB;
193                                         min-height: 2em;
194                                         border-radius: 10px;""")
195         self.pushButton_2.clicked.connect(self.update_securedrop)
196         self.update_thread = UpdateThread()
197         self.update_thread.signal.connect(self.update_status)
198         self.tails_thread = TailsconfigThread()
199         self.tails_thread.signal.connect(self.tails_status)
200         self.setup_thread = SetupThread()
201         self.setup_thread.signal.connect(self.setup_status)
202 
203     # At the end of this function, we will try to do tailsconfig.
204     # A new slot will handle tailsconfig output
205     def setup_status(self, result):
206         "This is the slot for setup thread"
207         self.output += result['output']
208         self.update_success = result['status']
209         self.failure_reason = result['failure_reason']
210         self.progressBar.setProperty("value", 60)
211         self.plainTextEdit.setPlainText(self.output)
212         self.plainTextEdit.setReadOnly = True
213         if not self.update_success:  # Failed to do setup
214             self.pushButton.setEnabled(True)
215             self.pushButton_2.setEnabled(True)
216             self.update_status_bar_and_output(self.failure_reason)
217             self.progressBar.setProperty("value", 0)
218             self.alert_failure(self.failure_reason)
219             return
220         self.progressBar.setProperty("value", 70)
221         self.call_tailsconfig()
222 
223     # This will update the output text after the git commands.
224     def update_status(self, result):
225         "This is the slot for update thread"
226         self.output += result['output']
227         self.update_success = result['status']
228         self.failure_reason = result['failure_reason']
229         self.progressBar.setProperty("value", 40)
230         self.plainTextEdit.setPlainText(self.output)
231         self.plainTextEdit.setReadOnly = True
232         if not self.update_success:  # Failed to do update
233             self.pushButton.setEnabled(True)
234             self.pushButton_2.setEnabled(True)
235             self.update_status_bar_and_output(self.failure_reason)
236             self.progressBar.setProperty("value", 0)
237             self.alert_failure(self.failure_reason)
238             return
239         self.progressBar.setProperty("value", 50)
240         self.update_status_bar_and_output(strings.doing_setup)
241         self.setup_thread.start()
242 
243     def update_status_bar_and_output(self, status_message):
244         """This method updates the status bar and the output window with the
245         status_message."""
246         self.statusbar.showMessage(status_message)
247         self.output += status_message + '\n'
248         self.plainTextEdit.setPlainText(self.output)
249 
250     def call_tailsconfig(self):
251         # Now let us work on tailsconfig part
252         if self.update_success:
253             # Get sudo password and add an enter key as tailsconfig command
254             # expects
255             sudo_password = self.get_sudo_password()
256             if not sudo_password:
257                 self.update_success = False
258                 self.failure_reason = strings.missing_sudo_password
259                 self.on_failure()
260                 return
261             self.tails_thread.sudo_password = sudo_password + '\n'
262             self.update_status_bar_and_output(strings.updating_tails_env)
263             self.tails_thread.start()
264         else:
265             self.on_failure()
266 
267     def tails_status(self, result):
268         "This is the slot for Tailsconfig thread"
269         self.output += result['output']
270         self.update_success = result['status']
271         self.failure_reason = result['failure_reason']
272         self.plainTextEdit.setPlainText(self.output)
273         self.progressBar.setProperty("value", 80)
274         if self.update_success:
275             # Remove flag file indicating an update is in progress
276             os.remove(FLAG_LOCATION)
277             self.update_status_bar_and_output(strings.finished)
278             self.progressBar.setProperty("value", 100)
279             self.alert_success()
280         else:
281             self.on_failure()
282 
283     def on_failure(self):
284         self.update_status_bar_and_output(self.failure_reason)
285         self.alert_failure(self.failure_reason)
286         # Now everything is done, enable the button.
287         self.pushButton.setEnabled(True)
288         self.pushButton_2.setEnabled(True)
289         self.progressBar.setProperty("value", 0)
290 
291     def update_securedrop(self):
292         if password_is_set():
293             self.pushButton_2.setEnabled(False)
294             self.pushButton.setEnabled(False)
295             self.progressBar.setProperty("value", 10)
296             self.update_status_bar_and_output(strings.fetching_update)
297             self.update_thread.start()
298         else:
299             self.pushButton_2.setEnabled(False)
300             pwd_err_dialog = QtWidgets.QMessageBox()
301             pwd_err_dialog.setText(strings.no_password_set_message)
302             pwd_err_dialog.exec()
303 
304     def alert_success(self):
305         self.success_dialog = QtWidgets.QMessageBox()
306         self.success_dialog.setIcon(QtWidgets.QMessageBox.Information)
307         self.success_dialog.setText(strings.finished_dialog_message)
308         self.success_dialog.setWindowTitle(strings.finished_dialog_title)
309         self.success_dialog.show()
310 
311     def alert_failure(self, failure_reason):
312         self.error_dialog = QtWidgets.QMessageBox()
313         self.error_dialog.setIcon(QtWidgets.QMessageBox.Critical)
314         self.error_dialog.setText(self.failure_reason)
315         self.error_dialog.setWindowTitle(strings.update_failed_dialog_title)
316         self.error_dialog.show()
317 
318     def get_sudo_password(self):
319         sudo_password, ok_is_pressed = QtWidgets.QInputDialog.getText(
320             self, "Tails Administrator password", strings.sudo_password_text,
321             QtWidgets.QLineEdit.Password, "")
322         if ok_is_pressed and sudo_password:
323             return sudo_password
324         else:
325             return None
326
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/journalist_gui/journalist_gui/SecureDropUpdater.py b/journalist_gui/journalist_gui/SecureDropUpdater.py
--- a/journalist_gui/journalist_gui/SecureDropUpdater.py
+++ b/journalist_gui/journalist_gui/SecureDropUpdater.py
@@ -7,6 +7,7 @@
 import pexpect
 import socket
 import sys
+import syslog as log
 
 from journalist_gui import updaterUI, strings, resources_rc  # noqa
 
@@ -35,9 +36,7 @@
         app.instance_binding.bind(IDENTIFIER)
     except OSError as e:
         if e.errno == ALREADY_BOUND_ERRNO:
-            err_dialog = QtWidgets.QMessageBox()
-            err_dialog.setText(name + strings.app_is_already_running)
-            err_dialog.exec()
+            log.syslog(log.LOG_NOTICE, name + strings.app_is_already_running)
             sys.exit()
         else:
             raise

{"golden_diff": "diff --git a/journalist_gui/journalist_gui/SecureDropUpdater.py b/journalist_gui/journalist_gui/SecureDropUpdater.py\n--- a/journalist_gui/journalist_gui/SecureDropUpdater.py\n+++ b/journalist_gui/journalist_gui/SecureDropUpdater.py\n@@ -7,6 +7,7 @@\n import pexpect\n import socket\n import sys\n+import syslog as log\n \n from journalist_gui import updaterUI, strings, resources_rc  # noqa\n \n@@ -35,9 +36,7 @@\n         app.instance_binding.bind(IDENTIFIER)\n     except OSError as e:\n         if e.errno == ALREADY_BOUND_ERRNO:\n-            err_dialog = QtWidgets.QMessageBox()\n-            err_dialog.setText(name + strings.app_is_already_running)\n-            err_dialog.exec()\n+            log.syslog(log.LOG_NOTICE, name + strings.app_is_already_running)\n             sys.exit()\n         else:\n             raise\n", "issue": "Graphical updater \"is already running\" dialog during late stages of the update process\n## Description\r\n\r\nThe graphical updater briefly shows an \"is already running\" dialog during the last stages of the process. The dialog can be dismissed and has no negative consequences.\r\n\r\nTested on Tails 3.16.\r\n\r\n## Steps to Reproduce\r\n\r\nPerform a full graphical update using the graphical updater, with the Tails admin password set.\r\n\r\n## Expected Behavior\r\n\r\nThe graphical update completes without any unusual messages.\r\n\r\n## Actual Behavior\r\n\r\nAn \"already running\" dialog briefly appears towards the end of the process.\n", "before_files": [{"content": "#!/usr/bin/python\nfrom PyQt5 import QtGui, QtWidgets\nfrom PyQt5.QtCore import QThread, pyqtSignal\nimport subprocess\nimport os\nimport re\nimport pexpect\nimport socket\nimport sys\n\nfrom journalist_gui import updaterUI, strings, resources_rc  # noqa\n\n\nFLAG_LOCATION = \"/home/amnesia/Persistent/.securedrop/securedrop_update.flag\"  # noqa\nESCAPE_POD = re.compile(r'\\x1B\\[[0-?]*[ -/]*[@-~]')\n\n\ndef password_is_set():\n\n    pwd_flag = subprocess.check_output(['passwd', '--status']).decode('utf-8').split()[1]\n\n    if pwd_flag == 'NP':\n        return False\n    return True\n\n\ndef prevent_second_instance(app: QtWidgets.QApplication, name: str) -> None:  # noqa\n\n    # Null byte triggers abstract namespace\n    IDENTIFIER = '\\0' + name\n    ALREADY_BOUND_ERRNO = 98\n\n    app.instance_binding = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)\n    try:\n        app.instance_binding.bind(IDENTIFIER)\n    except OSError as e:\n        if e.errno == ALREADY_BOUND_ERRNO:\n            err_dialog = QtWidgets.QMessageBox()\n            err_dialog.setText(name + strings.app_is_already_running)\n            err_dialog.exec()\n            sys.exit()\n        else:\n            raise\n\n\nclass SetupThread(QThread):\n    signal = pyqtSignal('PyQt_PyObject')\n\n    def __init__(self):\n        QThread.__init__(self)\n        self.output = \"\"\n        self.update_success = False\n        self.failure_reason = \"\"\n\n    def run(self):\n        sdadmin_path = '/home/amnesia/Persistent/securedrop/securedrop-admin'\n        update_command = [sdadmin_path, 'setup']\n\n        # Create flag file to indicate we should resume failed updates on\n        # reboot. Don't create the flag if it already exists.\n        if not os.path.exists(FLAG_LOCATION):\n            open(FLAG_LOCATION, 'a').close()\n\n        try:\n            self.output = subprocess.check_output(\n                update_command,\n                stderr=subprocess.STDOUT).decode('utf-8')\n            if 'Failed to install' in self.output:\n                self.update_success = False\n                self.failure_reason = strings.update_failed_generic_reason\n            else:\n                self.update_success = True\n        except subprocess.CalledProcessError as e:\n            self.output += e.output.decode('utf-8')\n            self.update_success = False\n            self.failure_reason = strings.update_failed_generic_reason\n        result = {'status': self.update_success,\n                  'output': self.output,\n                  'failure_reason': self.failure_reason}\n        self.signal.emit(result)\n\n\n# This thread will handle the ./securedrop-admin update command\nclass UpdateThread(QThread):\n    signal = pyqtSignal('PyQt_PyObject')\n\n    def __init__(self):\n        QThread.__init__(self)\n        self.output = \"\"\n        self.update_success = False\n        self.failure_reason = \"\"\n\n    def run(self):\n        sdadmin_path = '/home/amnesia/Persistent/securedrop/securedrop-admin'\n        update_command = [sdadmin_path, 'update']\n        try:\n            self.output = subprocess.check_output(\n                update_command,\n                stderr=subprocess.STDOUT).decode('utf-8')\n            if \"Signature verification successful\" in self.output:\n                self.update_success = True\n            else:\n                self.failure_reason = strings.update_failed_generic_reason\n        except subprocess.CalledProcessError as e:\n            self.update_success = False\n            self.output += e.output.decode('utf-8')\n            if 'Signature verification failed' in self.output:\n                self.failure_reason = strings.update_failed_sig_failure\n            else:\n                self.failure_reason = strings.update_failed_generic_reason\n        result = {'status': self.update_success,\n                  'output': self.output,\n                  'failure_reason': self.failure_reason}\n        self.signal.emit(result)\n\n\n# This thread will handle the ./securedrop-admin tailsconfig command\nclass TailsconfigThread(QThread):\n    signal = pyqtSignal('PyQt_PyObject')\n\n    def __init__(self):\n        QThread.__init__(self)\n        self.output = \"\"\n        self.update_success = False\n        self.failure_reason = \"\"\n        self.sudo_password = \"\"\n\n    def run(self):\n        tailsconfig_command = (\"/home/amnesia/Persistent/\"\n                               \"securedrop/securedrop-admin \"\n                               \"tailsconfig\")\n        try:\n            child = pexpect.spawn(tailsconfig_command)\n            child.expect('SUDO password:')\n            self.output += child.before.decode('utf-8')\n            child.sendline(self.sudo_password)\n            child.expect(pexpect.EOF)\n            self.output += child.before.decode('utf-8')\n            child.close()\n\n            # For Tailsconfig to be considered a success, we expect no\n            # failures in the Ansible output.\n            if child.exitstatus:\n                self.update_success = False\n                self.failure_reason = strings.tailsconfig_failed_generic_reason  # noqa\n            else:\n                self.update_success = True\n        except pexpect.exceptions.TIMEOUT:\n            self.update_success = False\n            self.failure_reason = strings.tailsconfig_failed_sudo_password\n\n        except subprocess.CalledProcessError:\n            self.update_success = False\n            self.failure_reason = strings.tailsconfig_failed_generic_reason\n        result = {'status': self.update_success,\n                  'output': ESCAPE_POD.sub('', self.output),\n                  'failure_reason': self.failure_reason}\n        self.signal.emit(result)\n\n\nclass UpdaterApp(QtWidgets.QMainWindow, updaterUI.Ui_MainWindow):\n\n    def __init__(self, parent=None):\n        super(UpdaterApp, self).__init__(parent)\n        self.setupUi(self)\n        self.statusbar.setSizeGripEnabled(False)\n        self.output = strings.initial_text_box\n        self.plainTextEdit.setPlainText(self.output)\n        self.update_success = False\n\n        pixmap = QtGui.QPixmap(\":/images/static/banner.png\")\n        self.label_2.setPixmap(pixmap)\n        self.label_2.setScaledContents(True)\n\n        self.progressBar.setProperty(\"value\", 0)\n        self.setWindowTitle(strings.window_title)\n        self.setWindowIcon(QtGui.QIcon(':/images/static/securedrop_icon.png'))\n        self.label.setText(strings.update_in_progress)\n\n        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab),\n                                  strings.main_tab)\n        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2),\n                                  strings.output_tab)\n\n        # Connect buttons to their functions.\n        self.pushButton.setText(strings.install_later_button)\n        self.pushButton.setStyleSheet(\"\"\"background-color: lightgrey;\n                                      min-height: 2em;\n                                      border-radius: 10px\"\"\")\n        self.pushButton.clicked.connect(self.close)\n        self.pushButton_2.setText(strings.install_update_button)\n        self.pushButton_2.setStyleSheet(\"\"\"background-color: #E6FFEB;\n                                        min-height: 2em;\n                                        border-radius: 10px;\"\"\")\n        self.pushButton_2.clicked.connect(self.update_securedrop)\n        self.update_thread = UpdateThread()\n        self.update_thread.signal.connect(self.update_status)\n        self.tails_thread = TailsconfigThread()\n        self.tails_thread.signal.connect(self.tails_status)\n        self.setup_thread = SetupThread()\n        self.setup_thread.signal.connect(self.setup_status)\n\n    # At the end of this function, we will try to do tailsconfig.\n    # A new slot will handle tailsconfig output\n    def setup_status(self, result):\n        \"This is the slot for setup thread\"\n        self.output += result['output']\n        self.update_success = result['status']\n        self.failure_reason = result['failure_reason']\n        self.progressBar.setProperty(\"value\", 60)\n        self.plainTextEdit.setPlainText(self.output)\n        self.plainTextEdit.setReadOnly = True\n        if not self.update_success:  # Failed to do setup\n            self.pushButton.setEnabled(True)\n            self.pushButton_2.setEnabled(True)\n            self.update_status_bar_and_output(self.failure_reason)\n            self.progressBar.setProperty(\"value\", 0)\n            self.alert_failure(self.failure_reason)\n            return\n        self.progressBar.setProperty(\"value\", 70)\n        self.call_tailsconfig()\n\n    # This will update the output text after the git commands.\n    def update_status(self, result):\n        \"This is the slot for update thread\"\n        self.output += result['output']\n        self.update_success = result['status']\n        self.failure_reason = result['failure_reason']\n        self.progressBar.setProperty(\"value\", 40)\n        self.plainTextEdit.setPlainText(self.output)\n        self.plainTextEdit.setReadOnly = True\n        if not self.update_success:  # Failed to do update\n            self.pushButton.setEnabled(True)\n            self.pushButton_2.setEnabled(True)\n            self.update_status_bar_and_output(self.failure_reason)\n            self.progressBar.setProperty(\"value\", 0)\n            self.alert_failure(self.failure_reason)\n            return\n        self.progressBar.setProperty(\"value\", 50)\n        self.update_status_bar_and_output(strings.doing_setup)\n        self.setup_thread.start()\n\n    def update_status_bar_and_output(self, status_message):\n        \"\"\"This method updates the status bar and the output window with the\n        status_message.\"\"\"\n        self.statusbar.showMessage(status_message)\n        self.output += status_message + '\\n'\n        self.plainTextEdit.setPlainText(self.output)\n\n    def call_tailsconfig(self):\n        # Now let us work on tailsconfig part\n        if self.update_success:\n            # Get sudo password and add an enter key as tailsconfig command\n            # expects\n            sudo_password = self.get_sudo_password()\n            if not sudo_password:\n                self.update_success = False\n                self.failure_reason = strings.missing_sudo_password\n                self.on_failure()\n                return\n            self.tails_thread.sudo_password = sudo_password + '\\n'\n            self.update_status_bar_and_output(strings.updating_tails_env)\n            self.tails_thread.start()\n        else:\n            self.on_failure()\n\n    def tails_status(self, result):\n        \"This is the slot for Tailsconfig thread\"\n        self.output += result['output']\n        self.update_success = result['status']\n        self.failure_reason = result['failure_reason']\n        self.plainTextEdit.setPlainText(self.output)\n        self.progressBar.setProperty(\"value\", 80)\n        if self.update_success:\n            # Remove flag file indicating an update is in progress\n            os.remove(FLAG_LOCATION)\n            self.update_status_bar_and_output(strings.finished)\n            self.progressBar.setProperty(\"value\", 100)\n            self.alert_success()\n        else:\n            self.on_failure()\n\n    def on_failure(self):\n        self.update_status_bar_and_output(self.failure_reason)\n        self.alert_failure(self.failure_reason)\n        # Now everything is done, enable the button.\n        self.pushButton.setEnabled(True)\n        self.pushButton_2.setEnabled(True)\n        self.progressBar.setProperty(\"value\", 0)\n\n    def update_securedrop(self):\n        if password_is_set():\n            self.pushButton_2.setEnabled(False)\n            self.pushButton.setEnabled(False)\n            self.progressBar.setProperty(\"value\", 10)\n            self.update_status_bar_and_output(strings.fetching_update)\n            self.update_thread.start()\n        else:\n            self.pushButton_2.setEnabled(False)\n            pwd_err_dialog = QtWidgets.QMessageBox()\n            pwd_err_dialog.setText(strings.no_password_set_message)\n            pwd_err_dialog.exec()\n\n    def alert_success(self):\n        self.success_dialog = QtWidgets.QMessageBox()\n        self.success_dialog.setIcon(QtWidgets.QMessageBox.Information)\n        self.success_dialog.setText(strings.finished_dialog_message)\n        self.success_dialog.setWindowTitle(strings.finished_dialog_title)\n        self.success_dialog.show()\n\n    def alert_failure(self, failure_reason):\n        self.error_dialog = QtWidgets.QMessageBox()\n        self.error_dialog.setIcon(QtWidgets.QMessageBox.Critical)\n        self.error_dialog.setText(self.failure_reason)\n        self.error_dialog.setWindowTitle(strings.update_failed_dialog_title)\n        self.error_dialog.show()\n\n    def get_sudo_password(self):\n        sudo_password, ok_is_pressed = QtWidgets.QInputDialog.getText(\n            self, \"Tails Administrator password\", strings.sudo_password_text,\n            QtWidgets.QLineEdit.Password, \"\")\n        if ok_is_pressed and sudo_password:\n            return sudo_password\n        else:\n            return None\n", "path": "journalist_gui/journalist_gui/SecureDropUpdater.py"}], "after_files": [{"content": "#!/usr/bin/python\nfrom PyQt5 import QtGui, QtWidgets\nfrom PyQt5.QtCore import QThread, pyqtSignal\nimport subprocess\nimport os\nimport re\nimport pexpect\nimport socket\nimport sys\nimport syslog as log\n\nfrom journalist_gui import updaterUI, strings, resources_rc  # noqa\n\n\nFLAG_LOCATION = \"/home/amnesia/Persistent/.securedrop/securedrop_update.flag\"  # noqa\nESCAPE_POD = re.compile(r'\\x1B\\[[0-?]*[ -/]*[@-~]')\n\n\ndef password_is_set():\n\n    pwd_flag = subprocess.check_output(['passwd', '--status']).decode('utf-8').split()[1]\n\n    if pwd_flag == 'NP':\n        return False\n    return True\n\n\ndef prevent_second_instance(app: QtWidgets.QApplication, name: str) -> None:  # noqa\n\n    # Null byte triggers abstract namespace\n    IDENTIFIER = '\\0' + name\n    ALREADY_BOUND_ERRNO = 98\n\n    app.instance_binding = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)\n    try:\n        app.instance_binding.bind(IDENTIFIER)\n    except OSError as e:\n        if e.errno == ALREADY_BOUND_ERRNO:\n            log.syslog(log.LOG_NOTICE, name + strings.app_is_already_running)\n            sys.exit()\n        else:\n            raise\n\n\nclass SetupThread(QThread):\n    signal = pyqtSignal('PyQt_PyObject')\n\n    def __init__(self):\n        QThread.__init__(self)\n        self.output = \"\"\n        self.update_success = False\n        self.failure_reason = \"\"\n\n    def run(self):\n        sdadmin_path = '/home/amnesia/Persistent/securedrop/securedrop-admin'\n        update_command = [sdadmin_path, 'setup']\n\n        # Create flag file to indicate we should resume failed updates on\n        # reboot. Don't create the flag if it already exists.\n        if not os.path.exists(FLAG_LOCATION):\n            open(FLAG_LOCATION, 'a').close()\n\n        try:\n            self.output = subprocess.check_output(\n                update_command,\n                stderr=subprocess.STDOUT).decode('utf-8')\n            if 'Failed to install' in self.output:\n                self.update_success = False\n                self.failure_reason = strings.update_failed_generic_reason\n            else:\n                self.update_success = True\n        except subprocess.CalledProcessError as e:\n            self.output += e.output.decode('utf-8')\n            self.update_success = False\n            self.failure_reason = strings.update_failed_generic_reason\n        result = {'status': self.update_success,\n                  'output': self.output,\n                  'failure_reason': self.failure_reason}\n        self.signal.emit(result)\n\n\n# This thread will handle the ./securedrop-admin update command\nclass UpdateThread(QThread):\n    signal = pyqtSignal('PyQt_PyObject')\n\n    def __init__(self):\n        QThread.__init__(self)\n        self.output = \"\"\n        self.update_success = False\n        self.failure_reason = \"\"\n\n    def run(self):\n        sdadmin_path = '/home/amnesia/Persistent/securedrop/securedrop-admin'\n        update_command = [sdadmin_path, 'update']\n        try:\n            self.output = subprocess.check_output(\n                update_command,\n                stderr=subprocess.STDOUT).decode('utf-8')\n            if \"Signature verification successful\" in self.output:\n                self.update_success = True\n            else:\n                self.failure_reason = strings.update_failed_generic_reason\n        except subprocess.CalledProcessError as e:\n            self.update_success = False\n            self.output += e.output.decode('utf-8')\n            if 'Signature verification failed' in self.output:\n                self.failure_reason = strings.update_failed_sig_failure\n            else:\n                self.failure_reason = strings.update_failed_generic_reason\n        result = {'status': self.update_success,\n                  'output': self.output,\n                  'failure_reason': self.failure_reason}\n        self.signal.emit(result)\n\n\n# This thread will handle the ./securedrop-admin tailsconfig command\nclass TailsconfigThread(QThread):\n    signal = pyqtSignal('PyQt_PyObject')\n\n    def __init__(self):\n        QThread.__init__(self)\n        self.output = \"\"\n        self.update_success = False\n        self.failure_reason = \"\"\n        self.sudo_password = \"\"\n\n    def run(self):\n        tailsconfig_command = (\"/home/amnesia/Persistent/\"\n                               \"securedrop/securedrop-admin \"\n                               \"tailsconfig\")\n        try:\n            child = pexpect.spawn(tailsconfig_command)\n            child.expect('SUDO password:')\n            self.output += child.before.decode('utf-8')\n            child.sendline(self.sudo_password)\n            child.expect(pexpect.EOF)\n            self.output += child.before.decode('utf-8')\n            child.close()\n\n            # For Tailsconfig to be considered a success, we expect no\n            # failures in the Ansible output.\n            if child.exitstatus:\n                self.update_success = False\n                self.failure_reason = strings.tailsconfig_failed_generic_reason  # noqa\n            else:\n                self.update_success = True\n        except pexpect.exceptions.TIMEOUT:\n            self.update_success = False\n            self.failure_reason = strings.tailsconfig_failed_sudo_password\n\n        except subprocess.CalledProcessError:\n            self.update_success = False\n            self.failure_reason = strings.tailsconfig_failed_generic_reason\n        result = {'status': self.update_success,\n                  'output': ESCAPE_POD.sub('', self.output),\n                  'failure_reason': self.failure_reason}\n        self.signal.emit(result)\n\n\nclass UpdaterApp(QtWidgets.QMainWindow, updaterUI.Ui_MainWindow):\n\n    def __init__(self, parent=None):\n        super(UpdaterApp, self).__init__(parent)\n        self.setupUi(self)\n        self.statusbar.setSizeGripEnabled(False)\n        self.output = strings.initial_text_box\n        self.plainTextEdit.setPlainText(self.output)\n        self.update_success = False\n\n        pixmap = QtGui.QPixmap(\":/images/static/banner.png\")\n        self.label_2.setPixmap(pixmap)\n        self.label_2.setScaledContents(True)\n\n        self.progressBar.setProperty(\"value\", 0)\n        self.setWindowTitle(strings.window_title)\n        self.setWindowIcon(QtGui.QIcon(':/images/static/securedrop_icon.png'))\n        self.label.setText(strings.update_in_progress)\n\n        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab),\n                                  strings.main_tab)\n        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2),\n                                  strings.output_tab)\n\n        # Connect buttons to their functions.\n        self.pushButton.setText(strings.install_later_button)\n        self.pushButton.setStyleSheet(\"\"\"background-color: lightgrey;\n                                      min-height: 2em;\n                                      border-radius: 10px\"\"\")\n        self.pushButton.clicked.connect(self.close)\n        self.pushButton_2.setText(strings.install_update_button)\n        self.pushButton_2.setStyleSheet(\"\"\"background-color: #E6FFEB;\n                                        min-height: 2em;\n                                        border-radius: 10px;\"\"\")\n        self.pushButton_2.clicked.connect(self.update_securedrop)\n        self.update_thread = UpdateThread()\n        self.update_thread.signal.connect(self.update_status)\n        self.tails_thread = TailsconfigThread()\n        self.tails_thread.signal.connect(self.tails_status)\n        self.setup_thread = SetupThread()\n        self.setup_thread.signal.connect(self.setup_status)\n\n    # At the end of this function, we will try to do tailsconfig.\n    # A new slot will handle tailsconfig output\n    def setup_status(self, result):\n        \"This is the slot for setup thread\"\n        self.output += result['output']\n        self.update_success = result['status']\n        self.failure_reason = result['failure_reason']\n        self.progressBar.setProperty(\"value\", 60)\n        self.plainTextEdit.setPlainText(self.output)\n        self.plainTextEdit.setReadOnly = True\n        if not self.update_success:  # Failed to do setup\n            self.pushButton.setEnabled(True)\n            self.pushButton_2.setEnabled(True)\n            self.update_status_bar_and_output(self.failure_reason)\n            self.progressBar.setProperty(\"value\", 0)\n            self.alert_failure(self.failure_reason)\n            return\n        self.progressBar.setProperty(\"value\", 70)\n        self.call_tailsconfig()\n\n    # This will update the output text after the git commands.\n    def update_status(self, result):\n        \"This is the slot for update thread\"\n        self.output += result['output']\n        self.update_success = result['status']\n        self.failure_reason = result['failure_reason']\n        self.progressBar.setProperty(\"value\", 40)\n        self.plainTextEdit.setPlainText(self.output)\n        self.plainTextEdit.setReadOnly = True\n        if not self.update_success:  # Failed to do update\n            self.pushButton.setEnabled(True)\n            self.pushButton_2.setEnabled(True)\n            self.update_status_bar_and_output(self.failure_reason)\n            self.progressBar.setProperty(\"value\", 0)\n            self.alert_failure(self.failure_reason)\n            return\n        self.progressBar.setProperty(\"value\", 50)\n        self.update_status_bar_and_output(strings.doing_setup)\n        self.setup_thread.start()\n\n    def update_status_bar_and_output(self, status_message):\n        \"\"\"This method updates the status bar and the output window with the\n        status_message.\"\"\"\n        self.statusbar.showMessage(status_message)\n        self.output += status_message + '\\n'\n        self.plainTextEdit.setPlainText(self.output)\n\n    def call_tailsconfig(self):\n        # Now let us work on tailsconfig part\n        if self.update_success:\n            # Get sudo password and add an enter key as tailsconfig command\n            # expects\n            sudo_password = self.get_sudo_password()\n            if not sudo_password:\n                self.update_success = False\n                self.failure_reason = strings.missing_sudo_password\n                self.on_failure()\n                return\n            self.tails_thread.sudo_password = sudo_password + '\\n'\n            self.update_status_bar_and_output(strings.updating_tails_env)\n            self.tails_thread.start()\n        else:\n            self.on_failure()\n\n    def tails_status(self, result):\n        \"This is the slot for Tailsconfig thread\"\n        self.output += result['output']\n        self.update_success = result['status']\n        self.failure_reason = result['failure_reason']\n        self.plainTextEdit.setPlainText(self.output)\n        self.progressBar.setProperty(\"value\", 80)\n        if self.update_success:\n            # Remove flag file indicating an update is in progress\n            os.remove(FLAG_LOCATION)\n            self.update_status_bar_and_output(strings.finished)\n            self.progressBar.setProperty(\"value\", 100)\n            self.alert_success()\n        else:\n            self.on_failure()\n\n    def on_failure(self):\n        self.update_status_bar_and_output(self.failure_reason)\n        self.alert_failure(self.failure_reason)\n        # Now everything is done, enable the button.\n        self.pushButton.setEnabled(True)\n        self.pushButton_2.setEnabled(True)\n        self.progressBar.setProperty(\"value\", 0)\n\n    def update_securedrop(self):\n        if password_is_set():\n            self.pushButton_2.setEnabled(False)\n            self.pushButton.setEnabled(False)\n            self.progressBar.setProperty(\"value\", 10)\n            self.update_status_bar_and_output(strings.fetching_update)\n            self.update_thread.start()\n        else:\n            self.pushButton_2.setEnabled(False)\n            pwd_err_dialog = QtWidgets.QMessageBox()\n            pwd_err_dialog.setText(strings.no_password_set_message)\n            pwd_err_dialog.exec()\n\n    def alert_success(self):\n        self.success_dialog = QtWidgets.QMessageBox()\n        self.success_dialog.setIcon(QtWidgets.QMessageBox.Information)\n        self.success_dialog.setText(strings.finished_dialog_message)\n        self.success_dialog.setWindowTitle(strings.finished_dialog_title)\n        self.success_dialog.show()\n\n    def alert_failure(self, failure_reason):\n        self.error_dialog = QtWidgets.QMessageBox()\n        self.error_dialog.setIcon(QtWidgets.QMessageBox.Critical)\n        self.error_dialog.setText(self.failure_reason)\n        self.error_dialog.setWindowTitle(strings.update_failed_dialog_title)\n        self.error_dialog.show()\n\n    def get_sudo_password(self):\n        sudo_password, ok_is_pressed = QtWidgets.QInputDialog.getText(\n            self, \"Tails Administrator password\", strings.sudo_password_text,\n            QtWidgets.QLineEdit.Password, \"\")\n        if ok_is_pressed and sudo_password:\n            return sudo_password\n        else:\n            return None\n", "path": "journalist_gui/journalist_gui/SecureDropUpdater.py"}]}