Skip to content

Commit

Permalink
Merge pull request #450 from freedomofpress/402-skip-updates-sometimes
Browse files Browse the repository at this point in the history
Skips running the updater if last update was good, a reboot isn't needed, and a config-specified delta isn't defined.
  • Loading branch information
conorsch authored Feb 18, 2020
2 parents 3d965da + c719c34 commit 9d4b6e4
Show file tree
Hide file tree
Showing 6 changed files with 262 additions and 52 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.1.3
0.1.4
48 changes: 41 additions & 7 deletions launcher/sdw-launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,33 @@
from sdw_updater_gui.UpdaterApp import UpdaterApp
from sdw_util import Util
from sdw_updater_gui import Updater

from sdw_updater_gui.UpdaterApp import launch_securedrop_client
from sdw_updater_gui.Updater import should_launch_updater
import logging
import sys
import argparse

DEFAULT_INTERVAL = 28800 # 8hr default for update interval


def parse_argv(argv):
parser = argparse.ArgumentParser()
parser.add_argument("--skip-delta", type=int)
return parser.parse_args(argv)


def launch_updater():
"""
Start the updater GUI
"""

app = QtGui.QApplication(sys.argv)
form = UpdaterApp()
form.show()
sys.exit(app.exec_())


def main():
def main(argv):
sdlog = logging.getLogger(__name__)
Util.configure_logging(Updater.LOG_FILE)
lock_handle = Util.obtain_lock(Updater.LOCK_FILE)
Expand All @@ -17,11 +38,24 @@ def main():
# Logged.
sys.exit(1)
sdlog.info("Starting SecureDrop Launcher")
app = QtGui.QApplication(sys.argv)
form = UpdaterApp()
form.show()
sys.exit(app.exec_())

args = parse_argv(argv)

try:
args.skip_delta
except NameError:
args.skip_delta = DEFAULT_INTERVAL

if args.skip_delta is None:
args.skip_delta = DEFAULT_INTERVAL

interval = int(args.skip_delta)

if should_launch_updater(interval):
launch_updater()
else:
launch_securedrop_client()


if __name__ == "__main__":
main()
main(sys.argv[1:])
71 changes: 71 additions & 0 deletions launcher/sdw_updater_gui/Updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,77 @@ def _safely_start_vm(vm):
sdlog.error(str(e))


def should_launch_updater(interval):
status = read_dom0_update_flag_from_disk(with_timestamp=True)

if _valid_status(status):
if _interval_expired(interval, status):
sdlog.info("Update interval expired: launching updater.")
return True
else:
if status["status"] == UpdateStatus.UPDATES_OK.value:
sdlog.info("Updates OK and interval not expired, launching client.")
return False
elif status["status"] == UpdateStatus.REBOOT_REQUIRED.value:
if last_required_reboot_performed():
sdlog.info(
"Required reboot performed, updating status and launching client."
)
_write_updates_status_flag_to_disk(UpdateStatus.UPDATES_OK)
return False
else:
sdlog.info("Required reboot pending, launching updater")
return True
elif status["status"] == UpdateStatus.UPDATES_REQUIRED.value:
sdlog.info(
"Updates are required, launching updater.".format(
str(status["status"])
)
)
return True
elif status["status"] == UpdateStatus.UPDATES_FAILED.value:
sdlog.info(
"Preceding update failed, launching updater.".format(
str(status["status"])
)
)
return True
else:
sdlog.info(
"Update status is unknown, launching updater.".format(
str(status["status"])
)
)
return True
else:
sdlog.info("Update status not available, launching updater.")
return True


def _valid_status(status):
"""
status should contain 2 items, the update flag and a timestamp.
"""
if isinstance(status, dict) and len(status) == 2:
return True
return False


def _interval_expired(interval, status):
"""
Check if specified update interval has expired.
"""

try:
update_time = datetime.strptime(status["last_status_update"], DATE_FORMAT)
except ValueError:
# Broken timestamp? run the updater.
return True
if (datetime.now() - update_time) < timedelta(seconds=interval):
return False
return True


class UpdateStatus(Enum):
"""
Standardizes return codes for update/upgrade methods
Expand Down
33 changes: 17 additions & 16 deletions launcher/sdw_updater_gui/UpdaterApp.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@
logger = logging.getLogger(__name__)


def launch_securedrop_client():
"""
Helper function to launch the SecureDrop Client
"""
try:
logger.info("Launching SecureDrop client")
subprocess.Popen(["qvm-run", "sd-app", "gtk-launch securedrop-client"])
except subprocess.CalledProcessError as e:
logger.error("Error while launching SecureDrop client")
logger.error(str(e))
sys.exit(0)


class UpdaterApp(QtGui.QMainWindow, Ui_UpdaterDialog):
def __init__(self, parent=None):
super(UpdaterApp, self).__init__(parent)
Expand All @@ -19,7 +32,7 @@ def __init__(self, parent=None):
self.setupUi(self)
self.clientOpenButton.setEnabled(False)
self.clientOpenButton.hide()
self.clientOpenButton.clicked.connect(self.launch_securedrop_client)
self.clientOpenButton.clicked.connect(launch_securedrop_client)
self.rebootButton.setEnabled(False)
self.rebootButton.hide()
self.rebootButton.clicked.connect(self.reboot_workstation)
Expand Down Expand Up @@ -173,19 +186,6 @@ def get_vms_that_need_upgrades(self, results):
vms_to_upgrade.append(vm)
return vms_to_upgrade

def launch_securedrop_client(self):
"""
Helper method to launch the SecureDrop Client
"""
try:
logger.info("Launching SecureDrop client")
subprocess.Popen(["qvm-run", "sd-app", "gtk-launch securedrop-client"])
except subprocess.CalledProcessError as e:
self.proposedActionDescription.setText(strings.descri)
logger.error("Error while launching SecureDrop client")
logger.error(str(e))
sys.exit(0)

def apply_all_updates(self):
"""
Method used by the applyUpdatesButton that will create and start an
Expand Down Expand Up @@ -246,10 +246,11 @@ def run(self):
results[vm] = result
self.progress_signal.emit(progress)

# write the flags to disk
# write the flags to disk after successful updates, including updates
# that require a reboot.
run_results = Updater.overall_update_status(results)
Updater._write_updates_status_flag_to_disk(run_results)
if run_results == UpdateStatus.UPDATES_OK:
if run_results in {UpdateStatus.UPDATES_OK, UpdateStatus.REBOOT_REQUIRED}:
Updater._write_last_updated_flags_to_disk()
# populate signal contents
message = results # copy all the information from results
Expand Down
Loading

0 comments on commit 9d4b6e4

Please sign in to comment.