diff --git a/CommonResources b/CommonResources index 83f70e2..f4bea91 100755 --- a/CommonResources +++ b/CommonResources @@ -412,6 +412,11 @@ endScript () if [ $scriptAction == 'INSTALL' ] ; then # assume that if we get this far, any command line opitons have already been set touch "$setupOptionsDir/optionsSet" + + # clear flag preventing auto installs in PackageManager + if $userInteraction ; then + rm -f "$setupOptionsDir/DO_NOT_AUTO_INSTALL" + fi # set up reinstallMods to run this script again after a VenusOS update if [ ! -f "$reinstallScriptsList" ] || [ $(grep -c "$fullScriptName" "$reinstallScriptsList") == 0 ]; then @@ -441,6 +446,10 @@ endScript () cp "$scriptDir/version" "$installedVersionFile" fi elif [ $scriptAction == 'UNINSTALL' ] ; then + # set flag preventing auto installs in PackageManager + if $userInteraction ; then + touch "$setupOptionsDir/DO_NOT_AUTO_INSTALL" + fi # remove this script from reinstallScriptsList to prevent further calls during boot if [ -f "$reinstallScriptsList" ] && [ ! $(grep -c "$fullScriptName" "$reinstallScriptsList") == 0 ]; then logMessage removing "$shortScriptName" from $(basename "$reinstallScriptsList") @@ -468,7 +477,7 @@ endScript () logMessage "platform not compatible - exiting" exit $EXIT_INCOMPATIBLE_PLATFOM elif $rebootNeeded ; then - if $logToConsole ; then + if $userInteraction ; then yesNoPrompt "Reboot system now (y) or do it manually later (n): " if $yesResponse ; then echo "rebooting ..." @@ -482,14 +491,14 @@ endScript () exit $EXIT_REBOOT fi elif $restartGui ; then - if $logToConsole ; then + if $userInteraction ; then yesNoPrompt "Restart the GUI now (y) or issue a do it manually later (n): " if $yesResponse ; then echo "restarting GUI ..." svc -t /service/gui exit $EXIT_SUCCESS else - echo "GUI must be restarted activate components" + echo "GUI must be restarted to activate components" exit $EXIT_RESTART_GUI fi else @@ -533,22 +542,19 @@ endScript () # # # logToConsole is set to true in the LogHandler script -# It is set to false here if the command line parameters suggest -# the setup script is being run without the aid of the command line interface -# in which case the user will not be prompted and apprporiate actions are taken automatically -# exit code returned by the setup script indicates additional actions that -# need to be taken by the caller (if any) or the reason the script could not be completed +# It is set to false here the 'auto' parameter is passed on the command line +# which indicates this script is NOT being run from the command line # collect command line options reinstall=false force=false deferReboot=false deferGuiRestart=false +userInteraction=true while [ $# -gt 0 ]; do case $1 in $reinstallParam) reinstall=true - logToConsole=false ;; "force") force=true @@ -561,11 +567,13 @@ while [ $# -gt 0 ]; do ;; "install") scriptAction='INSTALL' - logToConsole=false ;; "uninstall") scriptAction='UNINSTALL' + ;; + "auto") logToConsole=false + userInteraction=false ;; *) esac @@ -642,7 +650,6 @@ if $reinstall ; then else exit $EXIT_SUCCESS fi - logToConsole=false # not running from reinstallMods else diff --git a/DbusSettingsResources b/DbusSettingsResources index 3849d97..507f447 100755 --- a/DbusSettingsResources +++ b/DbusSettingsResources @@ -7,6 +7,8 @@ # need to make settings changes # These functions check to see if the settings system is operational and defer # the set/create/remove activity so the calling script may continue +# +# as of v2.90~3 changes to source "/data/SetupHelper/EssentialResources" source "/data/SetupHelper/LogHandler" diff --git a/FileSets/PageSettingsPackageManager.qml b/FileSets/PageSettingsPackageManager.qml index 653f593..03385ef 100644 --- a/FileSets/PageSettingsPackageManager.qml +++ b/FileSets/PageSettingsPackageManager.qml @@ -118,6 +118,15 @@ MbPage { description: qsTr("Backup & restore settings") subpage: Component { PageSettingsPmBackup {} } show: showControls + } + MbMountState { + description: qsTr("microSD / USB") + } + MbSubMenu + { + description: qsTr("Initialize PackageManager ...") + subpage: Component { PageSettingsPmInitialize {} } + show: showControls } } } diff --git a/FileSets/PageSettingsPmInitialize.qml b/FileSets/PageSettingsPmInitialize.qml new file mode 100644 index 0000000..f47e8f7 --- /dev/null +++ b/FileSets/PageSettingsPmInitialize.qml @@ -0,0 +1,52 @@ +/////// new menu for PackageManager initialize + +import QtQuick 1.1 +import "utils.js" as Utils +import com.victron.velib 1.0 + +MbPage { + id: root + title: pmRunning ? qsTr("Intialize Package Manager") : qsTr ("Package manager not running") + property string settingsPrefix: "com.victronenergy.settings/Settings/PackageManager" + property bool pmRunning: installStatus.valid + + property VBusItem editAction: VBusItem { bind: Utils.path(servicePrefix, "/GuiEditAction") } + VBusItem { id: installStatus; bind: Utils.path(servicePrefix, "/InstallStatus") } + property bool showInProgress: false + + onPmRunningChanged: { showInProgress = false } + + function sendInitialize () + { + // provide local confirmation of action - takes PackageManager too long + editAction.setValue ("INITIALIZE") + showInProgress = true + } + + model: VisualItemModel + { + MbItemText + { + id: info + text: qsTr ("Initializing PackageManager will reset persistent storage\nto an empty state\nGit Hub user and branch info is lost") + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + } + MbOK + { + description: qsTr("Initialize") + value: qsTr("Press to Initialize") + onClicked: sendInitialize () + writeAccessLevel: User.AccessInstaller + visible: ! showInProgress + } + MbItemText + { + id: initializingMessage + text: qsTr ("... initializing and restarting") + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + visible: showInProgress + } + } +} diff --git a/FileSets/v2.80~41-large-25/PageSettings.qml b/FileSets/v2.80-large-26/PageSettings.qml similarity index 100% rename from FileSets/v2.80~41-large-25/PageSettings.qml rename to FileSets/v2.80-large-26/PageSettings.qml diff --git a/FileSets/v2.80~41-large-25/PageSettings.qml.orig b/FileSets/v2.80-large-26/PageSettings.qml.orig similarity index 100% rename from FileSets/v2.80~41-large-25/PageSettings.qml.orig rename to FileSets/v2.80-large-26/PageSettings.qml.orig diff --git a/FileSets/v2.90~3/PageSettings.qml b/FileSets/v2.90~12/PageSettings.qml similarity index 100% rename from FileSets/v2.90~3/PageSettings.qml rename to FileSets/v2.90~12/PageSettings.qml diff --git a/FileSets/v2.90~3/PageSettings.qml.orig b/FileSets/v2.90~12/PageSettings.qml.orig similarity index 100% rename from FileSets/v2.90~3/PageSettings.qml.orig rename to FileSets/v2.90~12/PageSettings.qml.orig diff --git a/PackageManager.py b/PackageManager.py index 7473847..a1998c0 100755 --- a/PackageManager.py +++ b/PackageManager.py @@ -78,6 +78,9 @@ # 'remove' - remove package from list TBD ????? # 'reboot' - reboot # 'restartGui' - restart the GUI +# 'INITIALIZE' - install PackageManager's persistent storage (dbus Settings) +# so that the storage will be rebuilt when PackageManager restarts +# PackageManager will exit when this command is received # # the GUI must wait for PackageManager to signal completion of one operation before initiating another # @@ -254,6 +257,36 @@ # if no GitHub information is contained in the package, the user must add it manually via the GUI # in so automatic downloads from GitHub can occur # +# PackageManager has a mechnism for backing up and restoring settings: +# SOME dbus Settings +# custom icons +# backing up gui, SetupHelper and PackageManager logs +# +# PackageManager checks for several flag files on removable media: +# SETTINGS_AUTO_RESTORE +# Triggers automatically restore dbus Settings and custom icons +# A previous settings backup operation must have been performed +# This creates a settingsBackup fiile and icons folder on the removable media +# that is used by settings restore (manual or triggered by this flag +# +# AUTO_INSTALL_PACKAGES +# If present, any packages found in /data will be automatically installed +# This is identical behavior to turning on the auto install option in the PackageManager menu +# +# AUTO_UNINSTALL_PACKAGES +# As above but uninstalls INCLUDING SetupHelper !!!! +# +# AUTO_EJECT +# If present, all removable media is ejected after related "automatic" work is finished +# +# INITIALIZE_PACKAGE_MANAGER +# If present, the PackageManager's persistent storage (dbus Settings parameters) are initialized +# and PackageManager restarted +# On restart, PackageManager will rebuild the dbus Settings from packages found in /data +# Only custom Git Hub user and branch information is lost. +# +# A menu item with the same function as INITIALIZE_PACKAGE_MANAGER is also provided +# # classes/instances/methods: # AddRemoveClass # AddRemove (thread) @@ -264,8 +297,7 @@ # SetGuiEditAction () # UpdateStatus () # LocateRawDefaultPackage () -# handleGuiEditAction -# () +# handleGuiEditAction () # UpdatePackageCount () # various Gets and Sets for dbus parameters # TransferOldDbusPackageInfo @@ -324,6 +356,7 @@ # AutoRebootCheck () + import platform import argparse import logging @@ -373,6 +406,8 @@ global AllVersionsRefreshed +global InitializePackageManager + # PushAction # @@ -439,6 +474,11 @@ def PushAction (command=None, source=None): # set the flag - reboot is done in main_loop global GuiRestart GuiRestart = True + elif action == 'INITIALIZE': + logging.warning ( "received PackageManager INITIALIZE request from " + source) + # set the flag - Initialize will quit the main loop, then work is done in main + global InitializePackageManager + InitializePackageManager = True # ignore blank action - this occurs when PackageManager changes the action on dBus to 0 # which acknowledges a GUI action elif action == '': @@ -2517,7 +2557,7 @@ def InstallPackage ( self, packageName=None, source=None , direction='install' ) DbusIf.UpdateStatus ( message=direction + "ing " + packageName, where=sendStatusTo, logLevel=WARNING ) try: - proc = subprocess.Popen ( [ setupFile, direction, 'deferReboot', 'deferGuiRestart' ], + proc = subprocess.Popen ( [ setupFile, direction, 'deferReboot', 'deferGuiRestart', 'auto' ], stdout=subprocess.PIPE, stderr=subprocess.PIPE ) proc.wait() stdout, stderr = proc.communicate () @@ -2707,6 +2747,7 @@ def run (self): # # actual installation is handled in the InstallPackages run() thread + class MediaScanClass (threading.Thread): # transferPackage unpacks the archive and moves it into postion in /data @@ -2784,6 +2825,8 @@ def __init__(self): threading.Thread.__init__(self) self.MediaQueue = queue.Queue (maxsize = 10) # used only for STOP self.threadRunning = True + self.AutoInstallOverride = False + self.AutoUninstall = False # # settingsBackup @@ -2814,10 +2857,34 @@ def settingsBackup (self, backupPath): for line in listFile: setting = line.strip() try: - value = bus.get_object("com.victronenergy.settings", setting).GetValue() + value = bus.get_object("com.victronenergy.settings", setting).GetValue() + attributes = bus.get_object("com.victronenergy.settings", setting).GetAttributes() except: continue - backupSettings.write ( setting + '=' + str(value) + '\n' ) + dataType = type (value) + if dataType is dbus.Double: + typeId = 'f' + elif dataType is dbus.Int32 or dataType is dbus.Int64: + typeId = 'i' + elif dataType is dbus.String: + typeId = 's' + else: + typeId = '' + logging.error ("settingsBackup - invalid data type " + typeId + " - can't include parameter attributes " + setting) + + value = str ( value ) + default = str (attributes[0]) + min = str (attributes[1]) + max = str (attributes[2]) + silent = str (attributes[3]) + + # create entry with just settng path and value without a valid data type + if typeId == '': + line = ','.join ( [ setting, value ]) + '\n' + else: + line = ','.join ( [ setting, value, typeId, default, min, max, silent ]) + '\n' + + backupSettings.write (line) settingsCount += 1 backupSettings.close () @@ -2902,12 +2969,61 @@ def settingsRestore (self, backupPath): settingsCount = 0 with open (backupFile, 'r') as fd: for line in fd: - parts = line.strip().split ('=') + parameterExists = False + # ( setting path, value, attributes) + parts = line.strip().split (',') + numberOfParts = len (parts) + # full entry with attributes + if numberOfParts == 7: + typeId = parts[2] + default = parts[3] + min = parts[4] + max = parts[5] + silent = parts[6] + # only path and name - old settings file format + elif numberOfParts == 2: + typeId = '' + default = '' + min = '' + max = '' + silent = '' + else: + logging.error ("settingsRestore: invalid line in file " + line) + continue + + path = parts[0] + value = parts[1] try: - bus.get_object("com.victronenergy.settings", parts[0]).SetValue(parts[1]) + bus.get_object("com.victronenergy.settings", path).GetValue() + parameterExists = True except: pass - settingsCount += 1 + + if not parameterExists: + if typeId == '': + logging.error ("settingsRestore: no attributes in settingsBackup file - can't create " + path) + # parameter does not yet exist, create it + else: + # silent uses a different method + if silent == 1: + method = 'AddSettingSilent' + else: + method = 'AddSetting' + + try: + proc = subprocess.Popen ( [ 'dbus', '-y', 'com.victronenergy.settings', '/', method, '', + path, default, typeId, min, max ], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + proc.wait() + parameterExists = True + logging.warning ("settingsRestore: creating " + path) + except: + logging.error ("settingsRestore: settings create failed for " + path) + + # update parameter's value if it exists (or was just created) + if parameterExists: + bus.get_object("com.victronenergy.settings", path).SetValue (value) + settingsCount += 1 # restore logo overlays overlaySourceDir = backupPath + "/logoBackup" @@ -2955,6 +3071,10 @@ def run (self): separator = '/' root = "/media" archiveSuffix = ".tar.gz" + autoRestore = False + autoRestoreComplete = False + autoEject = False + bus = dbus.SystemBus() # list of accepted branch/version substrings acceptList = [ "-current", "-latest", "-main", "-test", "-debug", "-beta", "-install", @@ -2977,6 +3097,16 @@ def run (self): if command == 'STOP' or self.threadRunning == False: return + # automaticTransfers is used to signal when anything is AUTOMATICALLY + # transferred from or to removable media + # this includes: + # transfrring a package from removable media to /data + # performing an automatic settings restore + # Manually triggered operations do not update these operations + # manually triggered settings backup + # manually triggered settings restore + automaticTransfers = False + try: drives = os.listdir (root) except: @@ -2998,6 +3128,7 @@ def run (self): for drive in drives: drivePath = separator.join ( [ root, drive ] ) + # process settings backup and restore # check for settings backup file settingsBackupPath = root + "/" + drive @@ -3010,50 +3141,102 @@ def run (self): backupSettingsFileExists = False if backupMediaExists: + autoRestoreFile = settingsBackupPath + "/SETTINGS_AUTO_RESTORE" + if os.path.exists (autoRestoreFile): + autoRestore = True + + autoEjectFile = settingsBackupPath + "/AUTO_EJECT" + if os.path.exists (autoEjectFile): + autoEject = True + + initializeFile = settingsBackupPath + "/INITIALIZE_PACKAGE_MANAGER" + if os.path.exists (initializeFile): + global InitializePackageManager + InitializePackageManager = True + + + # set the auto install flag for use elsewhere + autoUnInstallFile = settingsBackupPath + "/AUTO_UNINSTALL_PACKAGES" + if os.path.exists (autoUnInstallFile): + self.AutoUninstall = True + self.AutoInstallOverride = False + + # set the auto install flag for use elsewhere + # auto Uninstall overrides auto install + if not self.AutoUninstall: + autoInstallFile = settingsBackupPath + "/AUTO_INSTALL_PACKAGES" + if os.path.exists (autoInstallFile): + self.AutoInstallOverride = True + backupProgress = DbusIf.GetBackupProgress () # GUI triggered backup if backupProgress == 1: DbusIf.SetBackupProgress (3) self.settingsBackup (settingsBackupPath) DbusIf.SetBackupProgress (0) - elif backupProgress == 2: + elif backupProgress == 2 or ( autoRestore and not autoRestoreComplete ): if backupSettingsFileExists: DbusIf.SetBackupProgress (4) self.settingsRestore (settingsBackupPath) + if autoRestore: + autoRestoreComplete = True + automaticTransfers = True DbusIf.SetBackupProgress (0) - if drive in alreadyScanned: - continue - - # check any file name ending with the achive suffix - # all others are skipped - for path in glob.iglob (drivePath + "/*" + archiveSuffix): - accepted = False - if os.path.isdir (path): - continue - else: + # if we've scanned this drive previously, it won't have any new packages to transfer + # so skip it to avoid doing it again + if drive not in alreadyScanned: + # check any file name ending with the achive suffix + # all others are skipped + for path in glob.iglob (drivePath + "/*" + archiveSuffix): accepted = False - baseName = os.path.basename (path) - # verify the file name contains one of the accepted branch/version identifiers - # if not found in the list, the archive is rejected - for accept in acceptList: - if accept in baseName: - accepted = True - break - # discovered what appears to be a valid archive - # unpack it, do further tests and move it to /data - if accepted: - self.transferPackage (path) - if self.threadRunning == False: - return + if os.path.isdir (path): + continue else: - logging.warning (path + " not a valid archive name - rejected") + accepted = False + baseName = os.path.basename (path) + # verify the file name contains one of the accepted branch/version identifiers + # if not found in the list, the archive is rejected + for accept in acceptList: + if accept in baseName: + accepted = True + break + # discovered what appears to be a valid archive + # unpack it, do further tests and move it to /data + if accepted: + if self.transferPackage (path): + automaticTransfers = True + + if self.threadRunning == False: + return + else: + logging.warning (path + " not a valid archive name - rejected") + # mark this drive so it won't get scanned again + # this prevents repeated installs + alreadyScanned.append (drive) + # end if drive not in alreadyScanned # end for path - - # mark this drive so it won't get scanned again - # this prevents repeated installs - alreadyScanned.append (drive) #end for drive + + # we have arrived at a point where all removable media has been scanned + # and all possible work has been done + + # eject removable media if work has been done and the + # the AUTO_EJECT flag file was fouund on removable media during the most recent scan + # NOTE: this ejects ALL removable media whether or not they are involved in transfers + if automaticTransfers and autoEject: + logging.warning ("automatic media transfers have occured, ejecting ALL removable media") + bus.get_object("com.victronenergy.logger", "/Storage/MountState").SetValue (2) + + if not backupMediaExists: + autoRestore = False + autoEject = False + autoRestoreComplete = False + self.AutoInstallOverride = False + self.AutoUninstall = False + InitializePackageManager = False + + # end while # end run () # end MediaScanClass @@ -3069,8 +3252,19 @@ def mainLoop(): global lastDownloadMode # initialized in main global currentDownloadMode # initialized in main global noActionCount + global MediaScan + global InitializePackageManager + global SetupHelperUninstall + + # auto uninstall triggered by AUTO_UNINSTALL_PACKAGES flag file on removable media + # exit mainLoop and do uninstall in main, then reboot + # skip all processing below ! + if MediaScan.AutoUninstall: + mainloop.quit() + return False delayStart = 0.0 + SetupHelperUninstall = False # detect download mode changes and switch back to fast scan lastDownloadMode = currentDownloadMode @@ -3103,6 +3297,7 @@ def mainLoop(): package = PackageClass.PackageList [PackageIndex] PackageIndex += 1 packageName = package.PackageName + package.UpdateFileFlagsAndVersions () # disallow operations on this package if anything is pending packageOperationOk = not package.DownloadPending and not package.InstallPending @@ -3113,7 +3308,7 @@ def mainLoop(): packageOperationOk = False # don't allow other operations if download was triggered if packageOperationOk and package.AutoInstallOk and package.FileSetOk and package.Incompatible == ''\ - and DbusIf.GetAutoInstall () and package.InstallVersionCheck (): + and ( DbusIf.GetAutoInstall () or MediaScan.AutoInstallOverride ) and package.InstallVersionCheck (): PushAction ( command='install' + ':' + packageName, source='AUTO' ) # check all packages before looking for reboot or GUI restart @@ -3139,9 +3334,9 @@ def mainLoop(): noActionCount += 1 # wait for two complete passes with nothing happening - # before triggerinng reboot or GUI restart + # before triggerinng reboot, GUI restart or initializing PackageManager Settings if noActionCount >= 2: - if SystemReboot: + if SystemReboot or InitializePackageManager or SetupHelperUninstall: # exit the main loop mainloop.quit() return False @@ -3164,6 +3359,60 @@ def mainLoop(): # continue the main loop return True + + +# uninstall a package with a direct call to it's setup script +# used to do a blind uninstall in main () below + +def directUninstall (packageName): + try: + setupFile = "/data/" + packageName + "/setup" + if os.path.isfile(setupFile)and os.access(setupFile, os.X_OK): + proc = subprocess.Popen ( [ setupFile, 'uninstall', 'deferReboot', 'deferGuiRestart', 'auto' ], + stdout=subprocess.PIPE, stderr=subprocess.PIPE ) + proc.wait() + stdout, stderr = proc.communicate () + # convert from binary to string + stdout = stdout.decode ().strip () + stderr = stderr.decode ().strip () + returnCode = proc.returncode + except: + pass + + +# remove all packages found in /data +# package must be a directory with a file named version +# with the first character of that file 'v' +# and an executale file named 'setup' must exist in the directory +# no other checks are made +# SetupHelper is NOT removed since it's running this service +# if found, returns true if it was found so it can be done later +# just before the program ends + +def removeAllPackages (): + deferredSetupHelperRemove = False + for path in os.listdir ("/data"): + packageDir = "/data/" + path + if not os.path.isdir (packageDir): + continue + packageName = path + + versionFile = packageDir + "/version" + try: + fd = open (versionFile, 'r') + version = fd.readline().strip() + fd.close () + except: + continue + if version[0] != 'v': + continue + if packageName == "SetupHelper": + deferredSetupHelperRemove = True + else: + directUninstall (packageName) + + return deferredSetupHelperRemove + # main # # ######## code begins here @@ -3176,13 +3425,15 @@ def main(): global GuiRestart global PackageIndex global noActionCount - + global InitializePackageManager + global SetupHelperUninstall SystemReboot = False GuiRestart = False + InitializePackageManager = False + SetupHelperUninstall = False PackageIndex = 0 noActionCount = 0 - # set logging level to include info level entries logging.basicConfig( format='%(levelname)s:%(message)s', level=logging.WARNING ) @@ -3315,20 +3566,31 @@ def main(): # this section of code runs only after the mainloop quits - # stop threads, remove service from dbus - if SystemReboot: + # auto uninstall triggered by AUTO_UNINSTALL_PACKAGES flag file on removable media + if MediaScan.AutoUninstall: + DbusIf.UpdateStatus ( message="UNINSTALLING ALL PACKAGES & REBOOTING ...", where='Download') + DbusIf.UpdateStatus ( message="UNINSTALLING ALL PACKAGES & REBOOTING ...", where='Editor' ) + logging.warning (">>>> UNINSTALLING ALL PACKAGES & REBOOTING...") + + + # remove all pacakges - returns True if SetupHelper was found and skipped + # so it can be done later + SetupHelperUninstall = removeAllPackages () + + elif SystemReboot: DbusIf.UpdateStatus ( message="REBOOTING ...", where='Download') DbusIf.UpdateStatus ( message="REBOOTING ...", where='Editor' ) logging.warning (">>>> REBOOTING: to complete package installation") + # stop threads, remove service from dbus logging.warning ("stopping threads") UpdateGitHubVersion.StopThread () DownloadGitHub.StopThread () InstallPackages.StopThread () AddRemove.StopThread () MediaScan.StopThread () - DbusIf.RemoveDbusService () + try: UpdateGitHubVersion.join (timeout=30.0) DownloadGitHub.join (timeout=30.0) @@ -3338,14 +3600,35 @@ def main(): logging.critical ("attempt to join threads failed - one or more threads failed to exit") pass + # if initializing PackageManager persistent storage, set PackageCount to 0 + # which will cause the package list to be rebuilt from packages found in /data + # user-specified Git Hub user and branch are lost + if InitializePackageManager: + DbusIf.DbusSettings['packageCount'] = 0 + + DbusIf.RemoveDbusService () + + # reboot with SetupHelper uninstall + if SetupHelperUninstall: + try: + logging.critical (">>>> uninstalling SetupHelper and exiting") + # schedule reboot for 30 seconds later since this script will die during the ininstall + # this should give enough time for the uninstall to finish before reboot + subprocess.Popen ( [ 'nohup', 'bash', '-c', 'sleep 30; shutdown -r now', '&' ], stdout=subprocess.PIPE, stderr=subprocess.PIPE ) + except: + pass + + directUninstall ("SetupHelper") + # check for reboot - if SystemReboot: + elif SystemReboot: try: proc = subprocess.Popen ( [ 'shutdown', '-r', 'now', 'rebooting to complete package installation' ] ) # for debug: proc = subprocess.Popen ( [ 'shutdown', '-k', 'now', 'simulated reboot - system staying up' ] ) except: logging.critical ("shutdown failed") + if SystemReboot or SetupHelperUninstall: # insure the package manager service doesn't restart when we exit # it will start up again after the reboot try: diff --git a/ReadMe b/ReadMe index 580b22b..0029607 100644 --- a/ReadMe +++ b/ReadMe @@ -25,28 +25,40 @@ By far, the easiest way to install SetupHelper is the "blind install" which requires no command-line interaction. 1) Download venus-data.tgz from the SetupHelper GitHub repo. + + Note: Mac OS and Safari are set by default to unzip packages. + The Open "safe" files after downloading (bottom of Safair Preferences General) + must be disabled in order to retain the zip file. + 2) copy it to the root of a freshly formatted SD card or USB memory stick 3) place the media in the GX device (Cerbo, CCGX, etc) - 4) reboot the GX device TWICE - allowing the system to display the GUI each time - after the second reboot, you should find the Package Manager menus - at the bottom of the Settings menu - 5) REMOVE THE MEDIA from the GX device + 4) reboot the GX device and allow the system to display the GUI + + if you are running Venus OS v2.90 and beyond: + you should find the Package Manager menu at the bottom of the Settings menu + you should remove the media at this point + mechanisms are in place to prevent reinstallation, but removal is still a good idea + + if you are running Venus OS prior to v2.90, perform these additional steps: + + 5) reboot the GX device a second time + 6) WHILE the GX device is booting, REMOVE THE MEDIA from the GX device VERY IMPORTANT to prevent the next reboot from starting the process all over again failure to do so could disable reinstalls following a Venus OS firmware update !!! - Note: Mac OS and Safari are set by default to unzip packages. - This Open "safe" files after downloading must be disabled in order to retain the - zip file. This option is at the bottom of Safair Preferences General + you should find the Package Manager menu at the bottom of the Settings menu + + + venus-data.tgz is available here: + https://github.com/kwindrem/SetupHelper/raw/main/venus-data.tgz + - CAUTION: This mechanism overwrites /data/rcS.local !!!! + CAUTION: prior to v2,90, this mechanism overwrites /data/rcS.local !!!! If you are using rcS.local to perform boot-time activities, /data/rcS.local must be recreated following this "blind" install Note that SetupHelper also uses /data/rcS.local for reinstallation following a firmware update so use caution in recreating rcS.local. - - venus-data.tgz is available here: - https://github.com/kwindrem/SetupHelper/raw/main/venus-data.tgz Another way to install SetupHelper is to use the following from the command line of the GX device: @@ -66,6 +78,63 @@ CAUTION: Note that removal does not actually remove the package so other setup scripts will continue to function. + +System Recovery: + +It is highly unlikely, but some users have reported a package install leaving their system unresponsive +or with a nonfuncitonal GUI (white screen). In this case, your options depend on the platform and the current state of the system. + +First, (as always) reboot +Second, see if you can access the PackageManager menu. If so, you can remove pacakges one at a time from there. + If you find an offeding package, post an issue to the GitHub repo for that package and include: + Platform (Cerbo, CCGX, Raspberry PI, etc) + Venus OS firmware version + attach logs if you can get to them + Remove SetupHelper last since once you do, you loose the PackageManager menus! + +Third, if you have terminal or ssh access, try running the package setup scripts to uninstall packages one at a time. + +Fourth, if you have GUI access again, try booting to the previous Venus OS version (in Stored backup formware) + Then perform a fresh Online firmware update to the latest version or use the .swu update via removable media. + These procedures are documented: https://www.victronenergy.com/media/pg/Cerbo_GX/en/gx---how-to-update-firmware.html + +Fifth, perform the Blind uninstall procedure below. + +Sixth, perform the Victron "restore factory default" procedure: + https://www.victronenergy.com/media/pg/CCGX/en/factory-reset.html + + Note: this will wipe out all settings and you'll need to reconfigure the GX device from scratch. + +Finally, if you are running on a Raspberry PI, you can reimage the system SD card. + + Note: this will wipe out all settings and you'll need to reconfigure the GX device from scratch. + + +Blind UNINSTALL: + +A blind uninstall mechanism is also provided which is similar to blind install described above. +This can be used in extremely rare ases where a package install leaves the system unaccessible +via GUI or ssh. +This mechanism is a LAST RESORT. +This will run all package setup scripts to uninstall that package from system files, + then remove the package from /data and /data/rcS.local. + +CAUTION: Do NOT run the Victron restore to factory defaults script if you have an unresponsive system + after installing one of my packages. Doing so will remove the script that restores the file system + to their factory settings. + + Firmware reinstall requires access to the GUI so if you still don't have a working GUI after + the Victron restore to factory defaults, then you are pretty much out of luck. + +The archive for this is named venus-data.UninstallPackages.tar.gz. + + 1) Copy venus-data.UninstallPackages.tar.gz to a USB memory stick or SD card + 2) Rename the copy to venus-data.tar.gz + 3) Plug the removable media into the GX device + 4) Reboot, wait 2 minutes and reboot a second time + 5) when the system automatically reboots after the second manual one, remove the media + + Description: There are two parts to SetupHelper: @@ -154,11 +223,60 @@ The Package Manager includes a set of menus on the GX device menu system Any logo files in /data/themes/overlay Setup script options in /data/setupOptions The parameters must exist to be saved - The parameters must exist to be updated during a restore + The parameters will be created and set to the backed up value during a restore Note: Victron is working on a more comprehensive mechanism but is not working reliably yet This part of PackageManager is temporary and will be removed when the Victron functionality is working + SETTINGS_AUTO_RESTORE: + An automatic settings restore will be performed when PackageManager if the file named + SETTINGS_AUTO_RESTORE is detected in the root of removable media + + CAUTION: LEAVING THIS FLASH DRIVE IN THE SYSTEM WILL TRIGGER A SETTINGS RESTORE WITH EVERY BOOT + YOU MUST REMOVE THE FLASH DRIVE AFTER AUTO RESTORR + + microSD / USB: + is a duplicate of the menu item in VRM online portal + it can be used to eject ALL removable media before physically removing them + NOTE: all media is ejected, so if you are using one for VRM logging, + you'll need to reboot or unplug, then replug that device. + + AUTO_EJECT: + If this flag file is found on any removable media, ALL removable media is ejected + after the media is scanned AND if any transferrers were performed: + transfer a package from the media (as an archive file) to /data + restore backup settings + this will NOT occur for + manual settings backup or restore + + Removable media can be corrupted if removed while the VRM logger is still writing to it + so the drive must be ejected to prevent corruption + A manual eject button is included in the PackageManager menu + This automatic eject is intended for unattended deployment and will only occur if the AUTO_EJECT file exists + Unfortunately, the eject mechanism ejects all removable media, not just a specific one. + The VRM logger automatically uses the first removable media found so there is no control over it, + + + AUTO_INSTALL_PACKAGES: + If the file AUTO_INSTALL_PACKAGES is found on removable media, packages will be installed + even if the Auto Install menu option is turned off. This is generally used only for system deployment (see below). + + AUTO_UNINSTALL_PACKAGES: + As above, but will uninstall all packages found in /data + This is useful if you have not command line access and end up with a GUI that is unresponsive + or just to clean up a system, returning it (almost) to factory defaults + This flag file oerrides AUTO_INSTALL_PACKAGES if both are present + + The system is rebooted after the uninstall all just to be sure there's nothing left behind. + + Note: this uses PackageManager, so if SetupHelper isn't installed it will do nothing + + INITIALZE_PACKAGE_MANAGER and menu item: + If present, the PackageManager's persistent storage (dbus Settings parameters) are initialized + and PackageManager restarted + On restart, PackageManager will rebuild the dbus Settings from packages found in /data + Only custom Git Hub user and branch information is lost. + Package editor: This menu facilitates: @@ -193,7 +311,6 @@ The Package Manager includes a set of menus on the GX device menu system however this will remove the Package Manager itself. Once removed, the Blind Install mechanism will be needed again !! - USB/SD updates: When the GX device is not connected to the internet, a USB flash drive or microSD card provides an install/upgrade path. @@ -216,4 +333,19 @@ USB/SD updates: Package Manager is quite content to transfer and install an older version! So make sure you have the latest version especially if your GX device does not have internet access. -If you are interested in the inner workings of Setup Manager and Package Manager, read: SetupHelperDescription (to be provided later) + + +System automatic configuration and package installation: + It is possible to use SetupHelper to set up a new system based on a template saved from a working system. + Setup the working system the way you want the new system to behave including custom icons, + then perform a Settings backup. + Remove the flash drive from the GX device and plug into a computer that has internet access. + Copy venus-data.tgz from the SetupHelper GitHub repo to the same flash drive. + If you wish packages to also be installed, copy the package -latest.tgz file from those repos as well. + Create SETTINGS_AUTO_RESTORE on the flash drive (contents don't matter - file may be empty). + Create AUTO_INSTALL_PACKAGES on the flash drive as well. + Place the flash drive into the GX device to be configured and reboot (once for v2.90 or twice for prior versions). + REMOVE THE FLASH DRIVE after you have verified that all packages have been installed (check Active packages in PackageManager). + + +If you are interested in the inner workings of Setup Manager and Package Manager or wish to create a package that can be managed by PackageManager, contact me through the issues part of SetupHelper on GitHub. diff --git a/ServiceResources b/ServiceResources index 5123124..2094a36 100755 --- a/ServiceResources +++ b/ServiceResources @@ -14,12 +14,17 @@ # functions that begin with _ skip checks and do not log activity + # starting with v2.80~10, services are stored in this directory which is overlayed onto /service # all services need to be added there rather than /service # Note: service calls (eg svc) are still made on /service/... # there is an unknown interaction between /service and the overlay source # so code here operates on both directories +# starting with v2.90~3, additions to /opt/victronenergy/service do not populate to /service +# a reboot is necessary ! +# tempFsOverlay at least for now, new services will be copied to both places !!!!!!!!! + victronServicesDir="/opt/victronenergy/service" overlayWorkDir="/run/overlays/service" if [ -d "$victronServicesDir" ]; then @@ -30,6 +35,16 @@ else serviceOverlay=false fi +versionStringToNumber "v2.90~3" +tempFsStartVersion=$versionNumber +versionStringToNumber $venusVersion +if (( $versionNumber >= $tempFsStartVersion )) ; then + tempFsOverlay=true +else + tempFsOverlay=false +fi + + # startService and stopService start and stop the service, respectively # the 'down' flag is also cleared/set to control service runs in the future # startService will cause the service to stop then start again !!! @@ -72,9 +87,15 @@ startService () _stopService () { touch "$serviceDir/$1/down" + if $tempFsOverlay ; then + touch "/service/$1/down" + fi svc -d "/service/$1" if [ -e "$serviceDir/$1/log" ]; then touch "$serviceDir/$1/log/down" + if $tempFsOverlay ; then + touch "/service/$1/log/down" + fi svc -d "/service/$1/log" fi } @@ -105,7 +126,9 @@ _removeService () # removing the service in the overlayed service directory doesn't remove it from /service # it needs to be removed from the overlay work directory also rm -rf "$serviceDir/$1" - if $serviceOverlay ; then + if $tempFsOverlay ; then + rm -rf "/service/$1" + elif $serviceOverlay ; then rm -rf "$overlayWorkDir/$1" fi } @@ -152,7 +175,11 @@ installService () # service not yet installed, COPY service directory to the active locaiton if [ ! -e "$serviceDir/$1" ]; then logMessage "installing $1 service" + sleep 5 cp -R "$scriptDir/service" "$serviceDir/$1" + if $tempFsOverlay ; then + cp -R "$scriptDir/service" "/service/$1" + fi # service already installed - only copy changed files, then restart service else restartService=true @@ -161,12 +188,18 @@ installService () cmp -s "$scriptDir/service/run" "$serviceDir/$1/run" > /dev/null if [ $? != 0 ]; then cp "$scriptDir/service/run" "$serviceDir/$1/run" + if $tempFsOverlay ; then + cp "$scriptDir/service/run" "/service/$1/run" + fi fi fi if [ -f "$scriptDir/service/log/run" ]; then cmp -s "$scriptDir/service/log/run" "$serviceDir/$1/log/run" > /dev/null if [ $? != 0 ]; then cp "$scriptDir/service/log/run" "$serviceDir/$1/log/run" + if $tempFsOverlay ; then + cp "$scriptDir/service/log/run" "/service/$1/log/run" + fi fi fi fi @@ -178,6 +211,7 @@ installService () # insure service starts up # with overlays, this doesn't happen if the service was removed then added again else + sleep 5 svc -u "/service/$1" fi } diff --git a/blindInstall b/blindInstall deleted file mode 100755 index 836a41d..0000000 --- a/blindInstall +++ /dev/null @@ -1,89 +0,0 @@ -#!/bin/bash - -# this script is part of an autoInstall archive which installs SetupHelper -# previous versions of blindInstall also installed GuiMods but has since been removed -# since PackageManager will install all package archives found on the SD/USB media so -# the result is the same (or actually better) -# -# the archive makes use of the Venus OS update-data.sh script -# archives named "venus-data.tar.gz" are unpacked during boot -# overriting content in /data -# -# the archive unpacks to /data/SetupHelper-blind -# then blindInstall compares versions between /data/SetupHelper-blind and the installed version of SetupHelper -# if blind is DIFFERENT, SetupHelper-blind replaces SetupHelper and the setup script is run -# -# for this archive, Venus must be rebooted a second time, causing this speial rcS.local to run, -# which calls blindInstall as a background task. -# -# a call to /data/SetupHelper/reinstallMods is appended to rcS.local by all setup scripts -# using SetupHelper CommonResources. -# That call is included in the blind install rcS.local so that if the media is left inserted -# subsequent reboots will still check for reinstalls -# -# the rcS.local from the blindInstall is removed when SetupHelper/setup is run to keep things clean -# SetupHelper/setup creates a new one -# -# the blindInstall script is run in the background so it can wait for dbus Settings resources -# to become available before running the package install scripts. -# - -source "/data/SetupHelper-blind/EssentialResources" -source "/data/SetupHelper-blind/LogHandler" - -# wait until dbus settings are active -while [ $(dbus -y | grep -c "com.victronenergy.settings") == 0 ]; do - logMessage "waiting for dBus settings" - sleep 1 -done - -sleep 2 - -setupHelperBlind='/data/SetupHelper-blind' -setupHelperStored='/data/SetupHelper' -setupHelperInstalledVersion='/etc/venus/installedVersion-SetupHelper' - -runSetup=false -if [ -d "$setupHelperBlind" ]; then - if [ -f "$setupHelperBlind/version" ]; then - versionStringToNumber $(cat "$setupHelperBlind/version") - blindVersion=$versionNumber - else - blindVersion=0 - fi - if [ -f "$setupHelperInstalledVersion" ]; then - versionStringToNumber $(cat "$setupHelperInstalledVersion") - installedVersion=$versionNumber - else - installedVersion=0 - fi - # no stored version of SetupHelper - if [ ! -d "$setupHelperStored" ]; then - runSetup=true - # versions differ - elif (( installedVersion != blindVersion )); then - runSetup=true - fi -else - logMessage "SetupHelper-blind not extracted from the media - nothing to do" -fi - -# get rid of the blind install rcS.local so it can't run again -# (unless the archive still exists on the media on next boot which will reinstall rcS.local) -# SetupHelper/setup will install the proper one -rm -f /data/rcS.local - -# move the extracted archive into position and run the setup script -if $runSetup ; then - logMessage "blind install installing SetupHelper" - - # replace SetupHelper with the one from the archive - rm -rf "$setupHelperStored" - mv "$setupHelperBlind" "$setupHelperStored" - - # run the setup script - "$setupHelperStored/setup" install -else - logMessage "blind install did NOT update SetupHelper (probably same version)" - rm -rf "$setupHelperBlind" -fi diff --git a/blindInstall/SetupHelperVersion b/blindInstall/SetupHelperVersion new file mode 100644 index 0000000..64937a8 --- /dev/null +++ b/blindInstall/SetupHelperVersion @@ -0,0 +1 @@ +v4.15 diff --git a/blindInstall/blindInstall.sh b/blindInstall/blindInstall.sh new file mode 100755 index 0000000..25f80c6 --- /dev/null +++ b/blindInstall/blindInstall.sh @@ -0,0 +1,87 @@ +#!/bin/bash + +# this script is part of a "blind install" archive which installs SetupHelper +# without user interaction. Simply inserting media into the GX device +# and rebooting once or twice (see below) will install SetupHelper +# +# the process makes use of the Venus OS update-data.sh script run during system boot +# archives named "venus-data.tgz" are unpacked during boot +# overriting matching content in /data +# +# this archive unpacks to: +# /data/SetupHelper-blind to avoid overwriting an existing copy of SetupHelper +# /data/rc for the pre/post scripts (not used prior to v2.90) +# /data/rcS.local (used prior to v2.90) +# (overwrites any current file - restored as of v2.90 but not before!) +# if versions of /data/SetupHelper-blind and the installed version of SetupHelper +# DIFFER, OR if SetupHelper is NOT INSTALLED, +# SetupHelper-blind replaces SetupHelper and the setup script is run +# +# prior to v2.90: +# the first reboot, unpacks the archive replacing the itmes listed above +# Venus must be rebooted a second time +# The second reboot: +# runs /data/rcS.local included in the archive +# rcS.local compares versions then runs blindInstall.sh if appropriate +# +# starting with v2.90: +# pre-hook.sh and post-hook.sh scripts are run before and after the archive is unpacked +# /data/rcS.local is saved in pre-hook.sh and restored in post-hook.sh. +# The /data/rcS.local file included in the archive is never executed +# In stead, post-hook.sh performs the version checks and calls blindInstall.sh +# if appropriate. This eliminates the second reboot ! +# In order to check versions prior to unpacking the archive, +# the SetupHelper version is duplicated in the rc folder which unpacks to /data +# BEFORE the SetupHelper-blind is unpacked. +# +# a call to /data/SetupHelper/reinstallMods is appended to rcS.local by all setup scripts +# using SetupHelper CommonResources. +# That call is included in the blind install rcS.local so that if the media is left inserted +# subsequent reboots will still check for reinstalls (applies only to firmwire before v2.90) +# +# the rcS.local from the blindInstall is removed at the end of blindInstall.sh +# SetupHelper/setup creates a new one +# +# blindInstall.sh is run in the background so it can wait for dbus Settings resources +# to become available before running the package install script. +# + +source "/data/SetupHelper-blind/EssentialResources" +source "/data/SetupHelper-blind/LogHandler" + +# wait until dbus settings are active +while [ $(dbus -y | grep -c "com.victronenergy.settings") == 0 ]; do + logMessage "waiting for dBus settings" + sleep 1 +done + +sleep 2 + +setupHelperBlind='/data/SetupHelper-blind' +setupHelperStored='/data/SetupHelper' +setupHelperInstalledVersion='/etc/venus/installedVersion-SetupHelper' +blindVersionFile="$setupHelperBlind/version" +installedVersionFile='/etc/venus/installedVersion-SetupHelper' + +# move the extracted archive into position and run the setup script +if [ -e "$setupHelperBlind" ]; then + if [ -e "$setupHelperStored" ]; then + logMessage "removing previous SetupHelper" + rm -rf "$setupHelperStored" + fi + logMessage "moving SetupHelper archive into position" + mv "$setupHelperBlind" "$setupHelperStored" +else + logMessage "can't move SetupHelper archive into position" +fi + +# run the setup script +if [ -f "$setupHelperStored/setup" ]; then + logMessage "installing SetupHelper" + "$setupHelperStored/setup" install auto +else + logMessage "error - can't install SetupHelper" +fi + +logMessage "end" + diff --git a/blindInstall/post-hook.sh b/blindInstall/post-hook.sh new file mode 100755 index 0000000..4ec3dd1 --- /dev/null +++ b/blindInstall/post-hook.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +# this script is part of a "blind install" archive which installs SetupHelper +# without user interaction. Simply inserting media into the GX device +# and rebooting once or twice (see below) will install SetupHelper +# +# the process makes use of the Venus OS update-data.sh script run during system boot +# archives named "venus-data.tgz" are unpacked during boot +# overriting matching content in /data +# +# this archive unpacks to: +# /data/SetupHelper-blind to avoid overwriting an existing copy of SetupHelper +# /data/rc for the pre/post scripts (not used prior to v2.90) +# /data/rcS.local (used prior to v2.90) +# (overwrites any current file - restored as of v2.90 but not before!) +# if versions of /data/SetupHelper-blind and the installed version of SetupHelper +# DIFFER, OR if SetupHelper is NOT INSTALLED, +# SetupHelper-blind replaces SetupHelper and the setup script is run +# +# prior to v2.90: +# the first reboot, unpacks the archive replacing the itmes listed above +# Venus must be rebooted a second time +# The second reboot: +# runs /data/rcS.local included in the archive +# rcS.local compares versions then runs blindInstall.sh if appropriate +# +# starting with v2.90: +# pre-hook.sh and post-hook.sh scripts are run before and after the archive is unpacked +# /data/rcS.local is saved in pre-hook.sh and restored in post-hook.sh. +# The /data/rcS.local file included in the archive is never executed +# In stead, post-hook.sh performs the version checks and calls blindInstall.sh +# if appropriate. This eliminates the second reboot ! +# In order to check versions prior to unpacking the archive, +# the SetupHelper version is duplicated in the rc folder which unpacks to /data +# BEFORE the SetupHelper-blind is unpacked. +# +# a call to /data/SetupHelper/reinstallMods is appended to rcS.local by all setup scripts +# using SetupHelper CommonResources. +# That call is included in the blind install rcS.local so that if the media is left inserted +# subsequent reboots will still check for reinstalls (applies only to firmwire before v2.90) +# +# the rcS.local from the blindInstall is removed at the end of blindInstall.sh +# SetupHelper/setup creates a new one +# +# blindInstall.sh is run in the background so it can wait for dbus Settings resources +# to become available before running the package install script. +# + +logMessage () +{ + echo "$*" + + echo "blind install post-hook.sh: $*" | tai64n >> /data/log/SetupHelper +} + + +logMessage "start" + +# restore /data/rcS.local from backup +if [ -f /data/rcS.local.orig ] ; then + mv /data/rcS.local.orig /data/rcS.local +fi + +# run the blind install script from the SetupHelper-blind +script="/data/SetupHelper-blind/blindInstall/blindInstall.sh" +if [ -f "$script" ]; then + logMessage "running blindInstall.sh" + nohup "$script" > /dev/null & +fi + diff --git a/blindInstall/pre-hook.sh b/blindInstall/pre-hook.sh new file mode 100755 index 0000000..7ad178b --- /dev/null +++ b/blindInstall/pre-hook.sh @@ -0,0 +1,102 @@ +#!/bin/bash +# +# this script is part of a "blind install" archive which installs SetupHelper +# without user interaction. Simply inserting media into the GX device +# and rebooting once or twice (see below) will install SetupHelper +# +# the process makes use of the Venus OS update-data.sh script run during system boot +# archives named "venus-data.tgz" are unpacked during boot +# overriting matching content in /data +# +# this archive unpacks to: +# /data/SetupHelper-blind to avoid overwriting an existing copy of SetupHelper +# /data/rc for the pre/post scripts (not used prior to v2.90) +# /data/rcS.local (used prior to v2.90) +# (overwrites any current file - restored as of v2.90 but not before!) +# if versions of /data/SetupHelper-blind and the installed version of SetupHelper +# DIFFER, OR if SetupHelper is NOT INSTALLED, +# SetupHelper-blind replaces SetupHelper and the setup script is run +# +# prior to v2.90: +# the first reboot, unpacks the archive replacing the itmes listed above +# Venus must be rebooted a second time +# The second reboot: +# runs /data/rcS.local included in the archive +# rcS.local compares versions then runs blindInstall.sh if appropriate +# +# starting with v2.90: +# pre-hook.sh and post-hook.sh scripts are run before and after the archive is unpacked +# /data/rcS.local is saved in pre-hook.sh and restored in post-hook.sh. +# The /data/rcS.local file included in the archive is never executed +# In stead, post-hook.sh performs the version checks and calls blindInstall.sh +# if appropriate. This eliminates the second reboot ! +# In order to check versions prior to unpacking the archive, +# the SetupHelper version is duplicated in the rc folder which unpacks to /data +# BEFORE the SetupHelper-blind is unpacked. +# +# a call to /data/SetupHelper/reinstallMods is appended to rcS.local by all setup scripts +# using SetupHelper CommonResources. +# That call is included in the blind install rcS.local so that if the media is left inserted +# subsequent reboots will still check for reinstalls (applies only to firmwire before v2.90) +# +# the rcS.local from the blindInstall is removed at the end of blindInstall.sh +# SetupHelper/setup creates a new one +# +# blindInstall.sh is run in the background so it can wait for dbus Settings resources +# to become available before running the package install script. +# + +logMessage () +{ + echo "$*" + + echo "blind install pre-hook.sh: $*" | tai64n >> /data/log/SetupHelper +} + + +logMessage "start" + +scriptDir="$( cd "$(dirname $0)" >/dev/null 2>&1 ; /bin/pwd -P )" +blindVersionFile="$scriptDir/SetupHelperVersion" +installedVersionFile='/etc/venus/installedVersion-SetupHelper' +setupHelperStored='/data/SetupHelper' + +doInstall=false +# SetupHelper is currently stored in /data +# check to see if it needs to be updated +if [ -d "$setupHelperStored" ]; then + if [ -f "$blindVersionFile" ]; then + blindVersion=$(cat "$blindVersionFile") + else + logMessage "Error: no blind version" + blindVersion="" + fi + if [ -f "$installedVersionFile" ]; then + installedVersion=$(cat "$installedVersionFile") + else + installedVersion="" + fi + + if [ "$installedVersion" != "$blindVersion" ]; then + doInstall=true + fi +# no SetupHelper found, skip version checks and install +else + doInstall=true +fi +echo "blindVersion $blindVersion installedVersion $installedVersion doInstall $doInstall" >> /data/blindLog +# returning with 0 will trigger unpacking and run post-hook.sh +if $doInstall ; then + # back up /data/rcS.local for restoration in post.sh + rm -rf /data/rcS.local.orig + if [ -r /data/rcS.local ] ; then + cp /data/rcS.local /data/rcS.local.orig + fi + logMessage "will do install" + exit 0 +# returning non-zero will prevent unpacking +# there won't be an archive to unpack andpost-hook.sh will NOT run +else + logMessage "skipping unpack and install" + exit -1 +fi diff --git a/blindInstall/rcS.local b/blindInstall/rcS.local new file mode 100755 index 0000000..ab1409b --- /dev/null +++ b/blindInstall/rcS.local @@ -0,0 +1,111 @@ +#!/bin/bash + +# this script is part of a "blind install" archive which installs SetupHelper +# without user interaction. Simply inserting media into the GX device +# and rebooting once or twice (see below) will install SetupHelper +# +# the process makes use of the Venus OS update-data.sh script run during system boot +# archives named "venus-data.tgz" are unpacked during boot +# overriting matching content in /data +# +# this archive unpacks to: +# /data/SetupHelper-blind to avoid overwriting an existing copy of SetupHelper +# /data/rc for the pre/post scripts (not used prior to v2.90) +# /data/rcS.local (used prior to v2.90) +# (overwrites any current file - restored as of v2.90 but not before!) +# if versions of /data/SetupHelper-blind and the installed version of SetupHelper +# DIFFER, OR if SetupHelper is NOT INSTALLED, +# SetupHelper-blind replaces SetupHelper and the setup script is run +# +# prior to v2.90: +# the first reboot, unpacks the archive replacing the itmes listed above +# Venus must be rebooted a second time +# The second reboot: +# runs /data/rcS.local included in the archive +# rcS.local compares versions then runs blindInstall.sh if appropriate +# +# starting with v2.90: +# pre-hook.sh and post-hook.sh scripts are run before and after the archive is unpacked +# /data/rcS.local is saved in pre-hook.sh and restored in post-hook.sh. +# The /data/rcS.local file included in the archive is never executed +# In stead, post-hook.sh performs the version checks and calls blindInstall.sh +# if appropriate. This eliminates the second reboot ! +# In order to check versions prior to unpacking the archive, +# the SetupHelper version is duplicated in the rc folder which unpacks to /data +# BEFORE the SetupHelper-blind is unpacked. +# +# a call to /data/SetupHelper/reinstallMods is appended to rcS.local by all setup scripts +# using SetupHelper CommonResources. +# That call is included in the blind install rcS.local so that if the media is left inserted +# subsequent reboots will still check for reinstalls (applies only to firmwire before v2.90) +# +# the rcS.local from the blindInstall is removed at the end of blindInstall.sh +# SetupHelper/setup creates a new one +# +# blindInstall.sh is run in the background so it can wait for dbus Settings resources +# to become available before running the package install script. +# + +logMessage () +{ + echo "blind install rcS.local: $*" | tai64n >> /data/log/SetupHelper +} + +logMessage "start" + +# remove the pre/post hook directory +# prior to v2.90, this script handles the install and /data/rc is unused +rm -rf "/data/rc" +sync + +setupHelperBlind='/data/SetupHelper-blind' +blindVersionFile="$setupHelperBlind/version" +installedVersionFile='/etc/venus/installedVersion-SetupHelper' +setupHelperStored='/data/SetupHelper' + +doInstall=false +# SetupHelper is currently stored in /data +# check to see if it needs to be updated +if [ -d "$setupHelperStored" ]; then + if [ -f "$blindVersionFile" ]; then + blindVersion=$(cat "$blindVersionFile") + else + logMessage "Error: no blind version" + blindVersion="" + fi + if [ -f "$installedVersionFile" ]; then + installedVersion=$(cat "$installedVersionFile") + else + installedVersion="" + fi + + if [ "$installedVersion" != "$blindVersion" ]; then + doInstall=true + fi +# no SetupHelper found, skip version checks and install +else + doInstall=true +fi + + +if $doInstall ; then + script="$setupHelperBlind/blindInstall/blindInstall.sh" + if [ -f "$script" ]; then + logMessage "running blindInstall.sh" + rm -f "/data/rcS.local" + sync + nohup "$script" > /dev/null & + fi +else + logMessage "no update needed" + rm -rf "$setupHelperBlind" + # SetupHelper reinstall all Venus mods + # this is here in case /data/rcS.local installed by SetupHelper is replaced + # by this one during the first part of the blind install process + scrpit="/data/SetupHelper/reinstallMods" + if [ -f "$script" ]; then + nohup "$script" > /dev/null & + fi +fi + +logMessage "end" diff --git a/blindInstall/rcS.localForUninstall b/blindInstall/rcS.localForUninstall new file mode 100755 index 0000000..297e90a --- /dev/null +++ b/blindInstall/rcS.localForUninstall @@ -0,0 +1,50 @@ +#!/bin/bash + +# this script is part of a "blind UNINSTALL" archive which +# UNINSTALLS AND REMOVES packages + +packageDir=/data/SetupHelper +script="$packageDir/setup" +if [ -e $script ]; then + $script uninstall + rm -r $packageDir + +packageDir=/data/GuiMods +script="$packageDir/setup" +if [ -e $script ]; then + $script uninstall + rm -r $packageDir + +packageDir=/data/GeneratorConnector +script="$packageDir/setup" +if [ -e $script ]; then + $script uninstall deferReboot deferGuiRestart auto + rm -r $packageDir + +packageDir=/data/RpiDisplaySetup +script="$packageDir/setup" +if [ -e $script ]; then + $script uninstall deferReboot deferGuiRestart auto + rm -r $packageDir + +packageDir=/data/RpiGpioSetup +script="$packageDir/setup" +if [ -e $script ]; then + $script uninstall deferReboot deferGuiRestart auto + rm -r $packageDir + +packageDir=/data/ShutDownMonitor +script="$packageDir/setup" +if [ -e $script ]; then + $script uninstall deferReboot deferGuiRestart auto + rm -r $packageDir + +packageDir=/data/VeCanSetup +script="$packageDir/setup" +if [ -e $script ]; then + $script uninstall deferReboot deferGuiRestart auto + rm -r $packageDir + +rm -f /data/rcS.local + +reboot diff --git a/changes b/changes index 95d991b..68cb9e6 100644 --- a/changes +++ b/changes @@ -1,3 +1,46 @@ +v4.15: + released - no changes + +v4.15~7: + added delays in install service so things get initialized properly + +v4.15~6: + added blind UNINSTALL via a special venus-data.tar.gz file + see instructions in the ReadMe + +v4.14~5: + added PackageManager persistent storage initialize + Both the INITIALZE_PACKAGE_MANAGER flag file on removable media + and a menu item has been added that will trigger the + PackageManager dbus Setting storageto be initialized, + then PackageManager restarted. + The storage is then rebuilt when PackageManager starts back up. + added UNNSTALL_ALL_PACKAGES removable media flag + If this file is found on removable media, PackageManager + will UNINSTALL ALL packages including SetupHelper + these additions help recover systems without a user interface to factory conditions, + including a blank or unresponsive GUI + +v4.14~4: + updated ReadMe + fixed: auto eject occured on manual settings restore + should be just AUTOMATIC restores + +v4.14~3: + add auto eject + fixed: couldn't backup or restore settings + +v4.14~2: + settings restore now creates missing parameters + rewrote blind install to use the pre/post hooks for v2.90 + blind install still works with prior Venus OS versions + added AUTO_INSTALL_PACKAGES flag file on removable media + functions same as enabling auto install in PackageManager menu + added support for new /service mechanisms in v2.90 + +v4.14~1: + add settings auto restore if SETTINGS_AUTO_RESTORE flag file exists on removable media + v4.13: add logs as part of settings backup diff --git a/reinstallMods b/reinstallMods index 1c713c1..991f6a4 100755 --- a/reinstallMods +++ b/reinstallMods @@ -36,6 +36,7 @@ while read -u 9 line ; do # strip command parameters to make sure the path/command exists command=$(awk '{print var $1}' <<< $line) if [ -f $command ] ; then + line=$(echo "$line auto") $line if [ $? == $exitReboot ] ; then logMessage "$command requested reboot" diff --git a/removeManagerSettings b/removeManagerSettings index f8c22e6..35d51cc 100755 --- a/removeManagerSettings +++ b/removeManagerSettings @@ -1,12 +1,13 @@ #!/bin/bash -# remove Settings associated with previous versions of SetupHelper -# and those of the current verison +# remove Settings associated with PackageManager # run this script to make a clean start source /data/SetupHelper/DbusSettingsResources +svc -d /service/PackageManager + removeDbusSettings /Settings/PackageManager/Count \ /Settings/PackageManager/GitHubAutoUpdate \ /Settings/PackageManager/GitHubAutoDownload \ @@ -77,3 +78,5 @@ removeDbusSettings /Settings/PackageManager/15/PackageName \ /Settings/PackageManager/19/PackageName \ /Settings/PackageManager/19/GitHubUser \ /Settings/PackageManager/19/GitHubBranch > /dev/null + +svc -u /service/PackageManager diff --git a/setup b/setup index f64f1fd..9d05b82 100755 --- a/setup +++ b/setup @@ -56,7 +56,6 @@ if [ $scriptAction == 'NONE' ] ; then fi if [ $scriptAction == 'INSTALL' ] ; then - updateActiveFile "$qmlDir/PageSettings.qml" updateActiveFile "$qmlDir/PageSettingsPackageManager.qml" updateActiveFile "$qmlDir/PageSettingsPackageVersions.qml" @@ -66,6 +65,7 @@ if [ $scriptAction == 'INSTALL' ] ; then updateActiveFile "$qmlDir/PageSettingsPackageAdd.qml" updateActiveFile "$qmlDir/MbDisplayDefaultPackage.qml" updateActiveFile "$qmlDir/PageSettingsPmBackup.qml" + updateActiveFile "$qmlDir/PageSettingsPmInitialize.qml" installService PackageManager @@ -82,6 +82,7 @@ if [ $scriptAction == 'UNINSTALL' ] ; then restoreActiveFile "$qmlDir/PageSettingsPackageAdd.qml" restoreActiveFile "$qmlDir/MbDisplayDefaultPackage.qml" restoreActiveFile "$qmlDir/PageSettingsPmBackup.qml" + restoreActiveFile "$qmlDir/PageSettingsPmInitialize.qml" removeService PackageManager diff --git a/venus-data.UninstallPackages.tar.gz b/venus-data.UninstallPackages.tar.gz new file mode 100644 index 0000000..282b191 Binary files /dev/null and b/venus-data.UninstallPackages.tar.gz differ diff --git a/venus-data.tgz b/venus-data.tgz index 78a9f3f..d1905f9 100644 Binary files a/venus-data.tgz and b/venus-data.tgz differ diff --git a/version b/version index 7ba2bc4..64937a8 100644 --- a/version +++ b/version @@ -1 +1 @@ -v4.13 +v4.15