diff --git a/FileSets/VersionIndependent/PageSettingsPackageManager.qml b/FileSets/VersionIndependent/PageSettingsPackageManager.qml index 5c7776e..9ebda33 100644 --- a/FileSets/VersionIndependent/PageSettingsPackageManager.qml +++ b/FileSets/VersionIndependent/PageSettingsPackageManager.qml @@ -6,16 +6,27 @@ import com.victron.velib 1.0 MbPage { id: root - title: qsTr("Package manager") + title: showControls ? qsTr("Package manager") : qsTr("Package manager not running") property string settingsPrefix: "com.victronenergy.settings/Settings/PackageManager" property string servicePrefix: "com.victronenergy.packageManager" property string bindVrmloggerPrefix: "com.victronenergy.logger" - VBusItem { id: pmStatus; bind: Utils.path(servicePrefix, "/PmStatus") } + VBusItem { id: pmStatusItem; bind: Utils.path(servicePrefix, "/PmStatus") } + property string pmStatus: pmStatusItem.valid ? pmStatusItem.value : "" VBusItem { id: mediaStatus; bind: Utils.path(servicePrefix, "/MediaUpdateStatus") } VBusItem { id: actionNeeded; bind: Utils.path(servicePrefix, "/ActionNeeded") } VBusItem { id: editAction; bind: Utils.path(servicePrefix, "/GuiEditAction") } property bool showMediaStatus: mediaStatus.valid && mediaStatus.value != "" - property bool showControls: pmStatus.valid + property bool showControls: pmStatusItem.valid + + // the last status message received from PackageManager is saved in lastStatus + // so there is some status to display when PackageManager quits + property string lastStatus: "" + + onPmStatusChanged: + { + if (pmStatusItem.valid) + lastStatus = pmStatus + } model: VisibleItemModel { @@ -24,12 +35,12 @@ MbPage { id: status text: { - if (! showControls) - return "Package manager not running" - else if (mediaStatus.valid && mediaStatus.value != "") + if (mediaStatus.valid && mediaStatus.value != "") return mediaStatus.value + else if (showControls) + return pmStatus else - return pmStatus.value + return lastStatus } wrapMode: Text.WordWrap horizontalAlignment: Text.AlignHCenter diff --git a/HelperResources/CommonResources b/HelperResources/CommonResources index 91c664f..e0497ea 100755 --- a/HelperResources/CommonResources +++ b/HelperResources/CommonResources @@ -395,9 +395,9 @@ forcePackageUninstall () # backupActiveFile makes a copy of the active file in file.orig # if the original file does not exist the NO_ORIG flag is set -# to allow restoreAcive file to remove the active file +# to allow restoreAciveFile to remove the active file # -# if the backup (.orig file) exists the backup is not ukpdated +# if the backup (.orig file) exists the backup is not updated # # $1 is the full path/file name to be backed up # @@ -410,18 +410,26 @@ backupActiveFile () if $installFailed ; then return 1 fi - - baseName=$(basename $1) - if [ -e "$1.orig" ] || [ -e "$1.NO_ORIG" ]; then - return 1 - elif [ ! -f "$1" ]; then - touch "$1.NO_ORIG" - return 0 - else - cp "$1" "$1.orig" - rm -f "$1.NO_ORIG" - return 0 - fi + local activeFile="$1" + local origFile="$activeFile.orig" + local noOrigFile="$activeFile.NO_ORIG" + + if [ -e "$activeFile" ]; then + if ! [ -e "$origFile" ]; then + cp "$activeFile" "$origFile" + rm -f "$noOrigFile" + return 0 + else + return 1 + fi + else + if ! [ -e "$noOrigFile" ]; then + touch "$noOrigFile" + return 0 + else + return 1 + fi + fi } @@ -506,11 +514,10 @@ updateActiveFile () return 1 fi - # separate source and replacement files specified local sourceFile="" local activeFile="" - local previousPackage="" - local doPatch=false + + # separate source and replacement files specified if [ $# == 2 ]; then if [ -f "$1" ]; then sourceFile="$1" @@ -523,31 +530,41 @@ updateActiveFile () else activeFile="$1" fi - local baseName=$(basename "$activeFile") - local packageList="$activeFile.package" + # replacement files are not needed for some versions # if so marked, leave original untouched - if [ -f "$fileSet/$baseName.USE_ORIGINAL" ]; then + if [ -e "$fileSet/$baseName.USE_ORIGINAL" ]; then return 1 + fi + + # if the location of the active file must exist + if [ ! -e "$(dirname "$activeFile")" ]; then + setInstallFailed $EXIT_FILE_SET_ERROR "path to $activeFile does not exist" + return 1 + fi + + local usePatchedFile=false + local patchedReplacement="$tempFileDir/$baseName.patchedForInstall" + local currentPatchFile="$tempFileDir/$baseName.currentPatch" # source file not specified separately - look for it in expected places - elif [ -z "$sourceFile" ]; then + if [ -z "$sourceFile" ]; then # first in temp files - patched file - if [ -f "$tempFileDir/$baseName.patchedForInstall" ]; then - sourceFile="$tempFileDir/$baseName.patchedForInstall" - doPatch=true + if [ -e "$patchedReplacement" ] && [ -e "$currentPatchFile" ]; then + sourceFile="$patchedReplacement" + usePatchedFile=true # then in temp files - replacement - elif [ -f "$tempFileDir/$baseName" ]; then + elif [ -e "$tempFileDir/$baseName" ]; then sourceFile="$tempFileDir/$baseName" # then in version-specific FileSet - elif [ -f "$fileSet/$baseName" ]; then + elif [ -e "$fileSet/$baseName" ]; then sourceFile="$fileSet/$baseName" # then in version-independent file set - elif [ -f "$versionIndependentFileSet/$baseName" ]; then + elif [ -e "$versionIndependentFileSet/$baseName" ]; then sourceFile="$versionIndependentFileSet/$baseName" - # then in FileSets - elif [ -f "$pkgFileSets/$baseName" ]; then + # then in FileSets (previous location of version-indepencent files) + elif [ -e "$pkgFileSets/$baseName" ]; then sourceFile="$pkgFileSets/$baseName" # nothing found - can't continue else @@ -556,152 +573,167 @@ updateActiveFile () fi fi - local pathToFile=$(dirname "$activeFile") - if [ ! -e "$pathToFile" ]; then - # fatal if GUI v1 not present and needed for install - if [[ "$pathToFile" == "/opt/victronenergy/gui/"* ]]; then - if $guiV1required ; then - setInstallFailed $EXIT_NO_GUI_V1 "$packageName requires GUI v1" - # GUI v1 not needed - proceed without updating active file - fi - # active file is not in GUI v1 part of file system - so this is fatal - else - setInstallFailed $EXIT_FILE_SET_ERROR "path to $activeFile does not exist" - fi - return 1 - fi - - # check for package conflicts - # loop through all names in .package file - # conflict if package names do not match and this is a replacement (not a patch) - matchFound=false + # can't continue if other packages modified this file and this is a replacement (not a patch) + local local packageList="$activeFile.package" + local previousPackage="" + local matchFound=false if [ -e "$packageList" ]; then previousPackages=$( cat "$packageList" ) for previousPackage in ${previousPackages[@]}; do - if [ $packageName == $previousPackage ]; then + if [ "$packageName" == "$previousPackage" ]; then matchFound=true - elif ! $doPatch ; then - setInstallFailed $EXIT_PACKAGE_CONFLICT "$(basename $activeFile) was already modfied by $previousPackage" + elif ! $usePatchedFile ; then + setInstallFailed $EXIT_PACKAGE_CONFLICT "$baseName was already modfied by $previousPackage" return 1 fi done fi - # add file to installed files list (used for uninstallAll) - # do this before actually modifying things just in case there's an error - # that way the uninstall is assured - echo "$activeFile" >> "$installedFilesList" + # add file to installed files list (used by uninstallAll) + if [ -e "$installedFilesList" ] \ + && (( $( grep -c "$activeFile" "$installedFilesList" ) == 0 )); then + echo "$activeFile" >> "$installedFilesList" + fi + + # save the current patch file for use during a future uninstall + local previousPatchFile="$previousPatchesDir/$baseName.patch" + if $usePatchedFile ; then + cp "$currentPatchFile" "$previousPatchFile" + # no patch file used for this update + else + rm -f "$previousPatchFile" + fi - # update package flag to prevent overwrites by other packages # if replacement, replace the .package file - if ! $doPatch ; then + if ! $usePatchedFile ; then echo $packageName > "$packageList" - # if patch and this package not in list, add add it + # if patch and this package not in list yet, add add it elif ! $matchFound ; then echo $packageName >> "$packageList" fi - # already updated - don't need to do any more - if [ -f "$activeFile" ]; then - if cmp -s "$sourceFile" "$activeFile" > /dev/null ; then - return 1 - fi - fi - - backupActiveFile "$activeFile" - # update the active file + # update the active file if needed # patched files have already incorporated current active file content - # so nothing diffrerent here - cp "$sourceFile" "$activeFile" - - # save the current patch file for use during an uninstall - currentPatchFile="$tempFileDir/$baseName.currentPatch" - previousPatchFile="$previousPatchesDir/$baseName.patch" - if [ -e "$currentPatchFile" ]; then - cp "$currentPatchFile" "$previousPatchFile" - # no patch file used for this update - # make sure restoreActiveFile doesn't try to reverse patch for uninstall - elif [ -e "$previousPatchFile" ]; then - rm -f "$previousPatchFile" + # so nothing diffrerent here + doUpdate=false + if ! [ -f "$activeFile" ] || ! cmp -s "$sourceFile" "$activeFile" ; then + backupActiveFile "$activeFile" + cp "$sourceFile" "$activeFile" + if [[ -x "$activeFile.orig" ]]; then + chmod +x "$activeFile" + fi + updateRestartFlags "$activeFile" + thisFileUpdated=true + return 0 + else + return 1 fi - updateRestartFlags "$activeFile" - thisFileUpdated=true - return 0 } # end updateActiveFile () -# restoreActiveFile moves the backup copy to the active location +# restoreActiveFile +# restores the active file to the content before this package modified it +# for replacements, the backup copy is moved to the active location # if the backup copy doesn't exist BUT the NO_ORIG flag is set # the active copy is deleted to restore the system to stock +# +# for patches, this package's changes are removed from the active file +# by reverse patching it +# +# if the reverse patch fails, the original is restored +# modifications from all other packages are also removed !! +# this is drastic but has best chance to leave the system without errors +# # $1 is the active name, the one to be backed up # # returns 0 if active file was restored, 1 if not # also sets thisFileUpdated for backwards compatibility with existing setup scripts # -# restore only if the package that updated this file is the current package -# failure is indicated in the return code but does not trigger as faliure - restoreActiveFile () { thisFileUpdated=false local activeFile="$1" - local baseName=$( basename "$activeFile" ) local packageList="$activeFile.package" # look for this package's name in .package list # and remove it if found matchFound=false remainingPackages="" if [ -f "$packageList" ]; then - previousPackages=$( cat "$packageList" ) + previousPackages=($( cat "$packageList" )) for previousPackage in ${previousPackages[@]}; do - if [ $packageName == $previousPackage ]; then + if [ "$packageName" == "$previousPackage" ]; then matchFound=true else remainingPackages+="$previousPackage " fi done - # no .package file - so OK to install + # no .package file - so proceed with removal anyway else matchFound=true fi - # if not found - nothing to uninstall + # if this package not found - nothing to do if ! $matchFound ; then return 1 fi - restoreOriginal=true - unpatchError=false - # no other packages in .package so remove it - # and restore the original + + local baseName=$( basename "$activeFile" ) + local previousPatchFile="$previousPatchesDir/$baseName.patch" + + reversePatchError=false + restoreOriginal=false + # no other packages so restore the original if [ -z "$remainingPackages" ] ; then - rm -f "$packageList" + restoreOriginal=true + # other packages have also modified the active file + # attempt to reverse patch the active file + # if success, move result into the active position + # DO NOT restore original else - # eliminate this package from .package list if there are others - grep -v "$packageName" "$packageList" | tee "$packageList" > /dev/null - - # attempt to reverse patch the active file - # if success, don't restore original - previousPatchFile="$previousPatchesDir/$baseName.patch" - if ! [ -e "$previousPatchFile" ]; then - logMessage "CRITICAL: $packageName $baseName - no prevoius patch file" - unpatchError=true - else + if [ -e "$previousPatchFile" ]; then tempFile="$tempFileDir/$baseName" cp "$activeFile" "$tempFile" - if $patch --reverse -o "$activeFile" "$tempFile" "$previousPatchFile" &> /dev/null ; then - restoreOriginal=false - # reverse patch failed - # original will be restored + if $patch --reverse -o "$activeFile.tmp" "$tempFile" "$previousPatchFile" &> /dev/null ; then + mv -f "$activeFile.tmp" "$activeFile" + thisFileUpdated=true else logMessage "CRITICAL: $packageName $baseName - reverse patch failed" - unpatchError=true + rm -f "$activeFile.tmp" + reversePatchError=true fi + else + logMessage "CRITICAL: $packageName $baseName - no prevoius patch file" + reversePatchError=true + fi + # if the reverse patch failed, remove mods from ALL packages + #### TODO: DRASTIC but can't think of a way around this + if $reversePatchError ; then + message1="CRITICAL: $package $baseName could not be uninstalled cleanly" + message2=" $remainingPackages must be uninstalled and reinstalled" + logMessage "$message1" + logMessage "$message2" + echo "$message1" >> "$scriptDir/patchErrors" + echo "$message2" >> "$scriptDir/patchErrors" + + # report errors to other packages as well + for otherPackage in $remainingPackages ; do + otherPkgPatchErrors="$packageRoot/$otherPackage/patchErrors" + echo "$message1" >> "$otherPkgPatchErrors" + echo "$otherPackage must be uninstalled and reinstalled" >> "$otherPkgPatchErrors" + # remove previous patch in other package(s) since it no longer applies + rm -f "$previousPatchesRoot/$otherPackage/$baseName.patch" + done + setInstallFailed $EXIT_PATCH_ERROR "patch error details were saved in $packageName/patchErrors" fi fi + + # always remove previous patch file + rm -f "$previousPatchFile" + # restore original if no other packages have modified this active file # or if reverse patch failed - if $restoreOriginal ; then + if $restoreOriginal || $reversePatchError ; then if [ -e "$activeFile.orig" ]; then mv -f "$activeFile.orig" "$activeFile" thisFileUpdated=true @@ -709,41 +741,24 @@ restoreActiveFile () rm -f "$activeFile" thisFileUpdated=true fi - fi - - #### TODO: DRASTIC but can't think of a way around this - #### TODO: how/when to erase this message for this package ????? - if $unpatchError ; then - message1="CRITICAL: $package $baseName could not be uninstalled cleanly" - message2=" $remainingPackages must be uninstalled and reinstalled" - logMessage "$message1" - logMessage "$message2" - # remove previous patch errors - and report this error - rm -f "$scriptDir/patchErrors" - echo "$message1" >> "$scriptDir/patchErrors" - echo "$message2" >> "$scriptDir/patchErrors" rm -f "$packageList" - # report errors to other packages as well - for otherPackage in $remainingPackages ; do - otherPkgPatchErrors="$packageRoot/$otherPackage/patchErrors" - rm -f "$otherPkgPatchErrors" - echo "$message1" >> "$otherPkgPatchErrors" - echo "$otherPackage must be uninstalled and reinstalled" >> "$otherPkgPatchErrors" - rm -f "$setupOptionsRoot/$otherPackage/previousPatches/$baseName.patch" - done - setInstallFailed $EXIT_PATCH_ERROR "patch error details were saved in $packageName/patchErrors" + # there are other packages, remove only this package from list + else + grep -v "$packageName" "$packageList" | tee "$packageList" > /dev/null fi - rm -f "$previousPatchFile" - # remove file from installed file list if [ -f "$installedFilesList" ]; then grep -v "$activeFile" "$installedFilesList" | tee "$installedFilesList" > /dev/null fi - updateRestartFlags "$activeFile" - return 0 + if $thisFileUpdated; then + updateRestartFlags "$activeFile" + return 0 + else + return 1 + fi } # end restoreActiveFile () @@ -1031,7 +1046,7 @@ installAllServices () # get list of services in the package's service directory if [ -d "$servicesDir" ]; then servicesList=( $( cd "$servicesDir"; ls -d * 2> /dev/null ) ) - if [ ! -z "$servicesList" ]; then + if ! [ -z "$servicesList" ]; then logMessage "installing services" for service in ${servicesList[@]} ; do if $installFailed; then break; fi @@ -1412,7 +1427,7 @@ while [ $# -gt 0 ]; do done # do after logToConsole is enabled/disabled abvove -logMessage "--- starting setup script $packageVersion action $scriptAction" +logMessage "--- starting setup script $packageVersion action: $scriptAction" # packages that require options to proceed unattended # must include the optionsRequried flag file in their package directory @@ -1487,12 +1502,21 @@ getFileLists "$pkgFileSets" # tempFileDir is removed in the exit trap above but it is in volatile storage so will be removed on boot anyway tempFileDir=$( mktemp -d ) -# patch files previously used to patch a file are stored in setupOptions -# so they survive a firmware update or package update -previousPatchesDir="$setupOptionsDir/previousPatches" +# patch files previously used to patch a file are stored in /etc/venus +# so they track the selected root fs and are erased when Venus OS is updated +previousPatchesRoot="/etc/venus/previousPatches" +previousPatchesDir="$previousPatchesRoot/$packageName" if ! [ -e "$previousPatchesDir" ]; then mkdir -p "$previousPatchesDir" fi +# relocate previous patch files +oldPreviousPatchesDir="$setupOptionsDir/previousPatches" +if [ -e "$oldPreviousPatchesDir" ]; then + logMessage "relocating previous patches" + mv "$oldPreviousPatchesDir"/* "$previousPatchesDir" + rm -rf "$oldPreviousPatchesDir" +fi +unset oldPreviousPatchesDir # do install pre-checks - skip if uninstalling if [ $scriptAction != 'UNINSTALL' ]; then @@ -1580,35 +1604,40 @@ if [ $scriptAction != 'UNINSTALL' ]; then sed -e 's/VisibleItemModel/VisualItemModel/' "$sourceFile" > "$tempFileDir/$baseName" done fi -fi # if [ $scriptAction != 'UNINSTALL' ] -# create the forward and reverse patched files -# used during the actual install and to test if the patch/reverse patch will succeed -# done here so PackageManager knows if this will be possible before starting the opeartion -# -# if this and other packages have both modified the active file, -# the patch from this package is first removed -# by reverse patching the active file with the PREVIOUS patch file -# the new patch is then applied -# a test reverse patch insures the patch can be removed in the future -# the patch file used for this patch is then saved so it can be used -# for the reverse patch on next install/uninstall -# -# if no other packages have modified the active file, -# the patch is applied to .orig file if exists -# rather than reverse patching the active file -# this maintains compatibility with packages installed with older versions of SetupHelper - - -if ! [ -z "$fileListPatched" ];then - patchErrors=() - for activeFile in ${fileListPatched[@]}; do - baseName=$( basename $activeFile ) - tempActiveFile="$tempFileDir/$baseName" - if [ -e "$activeFile" ] ; then + # create the forward and reverse patched files + # used during the actual install and to test if the patch/reverse patch will succeed + # done here so PackageManager knows if this will be possible before starting the install + # + # if this and other packages have both modified the active file, + # the patch from this package is first removed + # by reverse patching the active file with the PREVIOUS patch file + # the new patch is then applied + # a test reverse patch insures the patch can be removed in the future + # the patch file used for this patch is then saved so it can be used + # for the reverse patch on next install/uninstall + # + # if no other packages have modified the active file, + # the patch is applied to .orig file if exists + # rather than reverse patching the active file + # this maintains compatibility with packages installed with older versions of SetupHelper + + if ! $installFailed && ! [ -z "$fileListPatched" ];then + patchErrors=() + for activeFile in ${fileListPatched[@]}; do + baseName=$( basename $activeFile ) + tempActiveFile="$tempFileDir/$baseName" + currentPatchFile="$tempFileDir/$baseName.currentPatch" previousPatchFile="$previousPatchesDir/$baseName.patch" - # chek for this package and others in .package list + rm -f "$currentPatchFile" + + if ! [ -e "$activeFile" ] ; then + patchErrors+=( "$baseName no active file for patch" ) + continue + fi + + # check for this and other packages in .package list packageList="$activeFile.package" thisPackageInList=false otherPackagesInList=false @@ -1624,44 +1653,44 @@ if ! [ -z "$fileListPatched" ];then fi patchOk=false - + if $thisPackageInList; then - # only this package modified active flie - ignore any previous patch and patch .orig file + # only this package modified active file + # ignore any previous patch and patch .orig file if ! $otherPackagesInList ; then if [ -e "$activeFile.orig" ]; then cp "$activeFile.orig" "$tempActiveFile" patchOk=true else - patchErrors+=( "no original file for $baseName" ) + patchErrors+=( "$baseName no .orig file for patch" ) fi # this and others have modified the active file # attempt to remove the previous patch for this package # then patch the result - else + elif [ -e "$previousPatchFile" ]; then if $patch --reverse -o "$tempActiveFile" "$activeFile" "$previousPatchFile" &> /dev/null ; then patchOk=true # reverse patch failed else - patchErrors+=( "unable to remove previous patch from active file $baseName" ) + patchErrors+=( "$baseName unable to remove previous patch" ) fi + else + patchErrors+=( "$baseName no previous patch file" ) fi # this package has not previously modified the active file - # other packages may have modified the active file - # if not, there is no .orig file - # either way, patch the active file + # patch the active file else cp "$activeFile" "$tempActiveFile" patchOk=true fi patchSuccess=false - forwardPatched="$tempFileDir/$baseName".patchedForInstall - currentPatchFile="$tempFileDir/$baseName.currentPatch" # a suitable source for the patch was located above if $patchOk; then # attempt to patch the active file with any file ending in .patch # the first one that successfully creates a forward AND reverse patch is used # .patchedForInstall provides the patched file for updateActiveFile patchFiles=( $( ls "$patchSourceDir/$baseName"*.patch ) ) + forwardPatched="$tempActiveFile.patchedForInstall" for patchFile in ${patchFiles[@]};do if $patch --forward -o "$forwardPatched" "$tempActiveFile" "$patchFile" &> /dev/null ; then # forward patch succeeded - test reverse patch (both must succeed) @@ -1672,44 +1701,41 @@ if ! [ -z "$fileListPatched" ];then fi done if $patchSuccess ; then - # save this so patch file used for install can be saved for evenutal uninstall - # or to reverse patch prior to reinstall + # save this so forwardPatched (created above) is used for install + # when file is installed, currentPatchFile will be copied to previousPatchFile + # for a future uninstall or reinstall cp "$patchFile" "$currentPatchFile" else - patchErrors+=( "patch unsuccesful for $baseName" ) + patchErrors+=( "$baseName patch unsuccesful" ) + rm -f "$forwardPatched" fi else - patchErrors+=( "no patch source for $baseName" ) + patchErrors+=( "$baseName no patch source" ) fi - if ! $patchSuccess ; then - rm -f "$forwardPatched" - rm -f "$currentPatchFile" + done # for activeFile + + # save errors in patchErrors file + if ! [ -z "$patchErrors" ] ; then + rm -f "$scriptDir/patchErrors" + for patchError in "${patchErrors[@]}"; do + logMessage "$patchError" + echo "$patchError" >> "$scriptDir/patchErrors" + done + setInstallFailed $EXIT_PATCH_ERROR "patch error details were saved in $packageName/patchErrors" + + # pre-checks only - direct exit + if [ $scriptAction == 'CHECK' ]; then + exit $EXIT_PATCH_ERROR + # script will exit since we are still in pre-checks !! + else + endScript fi + # no errors - remove the error history else - patchErrors+=( "$baseName no active file for patch" ) - continue - fi - done # for activeFile - - # save errors in patchErrors file - if ! [ -z "$patchErrors" ] ; then - rm -f "$scriptDir/patchErrors" - for patchError in "${patchErrors[@]}"; do - logMessage "$patchError" - echo "$patchError" >> "$scriptDir/patchErrors" - done - # script will exit since we are still in pre-checks !! - setInstallFailed $EXIT_PATCH_ERROR "patch error details were saved in $packageName/patchErrors" - if [ $scriptAction == 'CHECK' ]; then - exit $EXIT_PATCH_ERROR - else - endScript + rm -f "$scriptDir/patchErrors" fi - # no errors - remove the error history - else - rm -f "$scriptDir/patchErrors" - fi -fi # if fileListPatched + fi # if fileListPatched +fi # if [ $scriptAction != 'UNINSTALL' ] # go no further if just checking if [ $scriptAction == 'CHECK' ]; then diff --git a/HelperResources/ServiceResources b/HelperResources/ServiceResources index 56db015..38d2a89 100755 --- a/HelperResources/ServiceResources +++ b/HelperResources/ServiceResources @@ -217,7 +217,7 @@ installService () # that way the uninstall is assured echo "$serviceName" >> "$installedServicesList" - # service not yet installed, COPY service directory to the service directory(s) + # service not yet installed, COPY service's directory (run files) to the service directory(s) if [ ! -e "/service/$serviceName" ]; then logMessage "installing $serviceName service" @@ -239,7 +239,10 @@ installService () serviceRunning=true break fi - echo "waiting for $serviceName service to start" + # only report wait once + if (( delayCount == 10 )); then + echo "waiting for $serviceName service to start" + fi sleep 1 (( delayCount-- )) done @@ -266,13 +269,7 @@ installService () fi fi if serviceIsUp $serviceName ; then - if $runFromPm && [ $serviceName == 'PackageManager' ]; then - logMessage "$serviceName restart deferred until GUI restart" - restartGui=true - else - logMessage "restarting $serviceName service" - svc -t "/service/$serviceName" - fi + svc -t "/service/$serviceName" fi fi # log needs to be handled separtely including a restart diff --git a/PackageManager.py b/PackageManager.py index cc35af1..42372ec 100755 --- a/PackageManager.py +++ b/PackageManager.py @@ -357,6 +357,7 @@ DEBUG = 10 import sys +import signal import subprocess import threading import os @@ -708,7 +709,6 @@ def __init__(self): # StopThread () is called to shut down the thread def StopThread (self): - logging.warning ("attempting to stop AddRemove thread") self.threadRunning = False self.AddRemoveQueue.put ( ('STOP', ''), block=False ) @@ -1974,7 +1974,7 @@ def UpdateVersionsAndFlags (self, doConflictChecks=False, doScriptPreChecks=Fals for item in file: parts = item.split () if len (parts) < 2: - logging.error ("package dependency " + item + " requires a package name and a requirement") + logging.error ("package dependency " + item + " incomplete") continue dependencyPackage = parts [0] dependencyRequirement = parts [1] @@ -2247,7 +2247,6 @@ def SetPriorityGitHubVersion (self, command): # when run returns, the main method should catch the tread with join () def StopThread (self): - logging.warning ("attempting to stop UpdateGitHubVersion thread") self.threadRunning = False self.SetPriorityGitHubVersion ( 'STOP' ) @@ -2593,7 +2592,6 @@ def DownloadVersionCheck (self, package): # StopThread () is called to shut down the thread def StopThread (self): - logging.warning ("attempting to stop DownloadGitHub thread") self.threadRunning = False self.DownloadQueue.put ( ('STOP', ''), block=False ) @@ -2941,7 +2939,6 @@ def ResolveConflicts ( self, packageName=None, source=None ): # StopThread () is called to shut down the thread def StopThread (self): - logging.warning ("attempting to stop InstallPackages thread") self.threadRunning = False self.InstallQueue.put ( ('STOP', ''), block=False ) @@ -3376,7 +3373,6 @@ def settingsRestore (self, backupPath, settingsOnly = False): # this gives other threads time away from slower media scanning operations def StopThread (self): - logging.warning ("attempting to stop MediaScan thread") self.threadRunning = False self.MediaQueue.put ( "STOP", block=False ) @@ -3598,14 +3594,10 @@ def run (self): # (it would take 10 seconds to scan 10 packages) # # PackageManager is responsible for reinstalling packages following a firmware update -# reinstallMods is a script called from /data/rcS.local that installs SetupHelper -# including the PackageManager service -# to avoid conflicts, there are two flag files that handshake reinstallMods and PackageManager -# /etc/venus/REINSTALL_PACKAGES is set by reinstallMods -# to notify Package manager to reinstall all packages -# PackageManager clears that flag when all packages have been reinstalled -# /etc/venus/REINSTALL_MODS_RUNNING is set and cleared by reinstallMods -# PackageManager tests this flag and holds off scanning for installs while it is set +# reinstallMods is a script called from /data/rcS.local that installs the PackageManager service +# then sets the /etc/venus/REINSTALL_PACKAGES flag file instructing PackageManager +# to do a boot-time check all packages for possible reinstall +# PackageManager clears that flag when all packages have been reinstalled # boot-time reinstall is done using the normal automatic install mechanism but bypasses # the test for the user selectable auto install on/off # @@ -3722,30 +3714,23 @@ def mainLoop (): return False actionMessage = "" - statusMessage = "" bootReinstallFile="/etc/venus/REINSTALL_PACKAGES" # hold off all package processing if package list is empty emptyPackageList = False - reinstallModsRunning = False if len (PackageClass.PackageList) == 0: emptyPackageList = True holdOffScan = True - # hold off processing if reinstallMods is running to prevent conflicts - # probalby never happen but just in case - elif os.path.exists ("/etc/venus/REINSTALL_MODS_RUNNING"): - reinstallModsRunning = True - holdOffScan = True # if boot-time reinstall has been requiested by reinstallMods # override modes and initiate auto install of all packages elif os.path.exists (bootReinstallFile): # beginning of boot install - reset package index to insure a complete scan if not bootInstall: + bootInstall = True packageIndex = 0 logging.warning ("starting boot-time reinstall") - bootInstall = True currentDownloadMode = AUTO_DOWNLOADS_OFF lastDownloadMode = AUTO_DOWNLOADS_OFF autoInstall = True @@ -3797,7 +3782,7 @@ def mainLoop (): # disallow operations on this package if anything is pending packageOperationOk = not package.DownloadPending and not package.InstallPending - if packageOperationOk and currentDownloadMode != AUTO_DOWNLOADS_OFF\ + if packageOperationOk and currentDownloadMode != AUTO_DOWNLOADS_OFF \ and DownloadGitHub.DownloadVersionCheck (package): # don't allow install if download is needed - even if it has not started yet packageOperationOk = False @@ -3807,26 +3792,19 @@ def mainLoop (): # validate package for install if packageOperationOk and package.Incompatible == "" : installOk = False - oneTimeInstall = False + # one-time install flag file is set in package directory - install without further checks oneTimeInstallFile = "/data/" + packageName + "/ONE_TIME_INSTALL" if os.path.exists (oneTimeInstallFile): os.remove (oneTimeInstallFile) - oneTimeInstall = True - elif autoInstall: installOk = True - else: - autoInstallFile = "/data/" + packageName + "/AUTO_INSTALL" - if os.path.exists (autoInstallFile): + # auto install OK (not manually uninstalled) and versions are different + elif package.AutoInstallOk and package.PackageVersionNumber != package.InstalledVersionNumber: + if autoInstall: + installOk = True + elif os.path.exists ("/data/" + packageName + "/AUTO_INSTALL"): installOk = True - # block install if manually uninstalled or if versions are the same - if not package.AutoInstallOk: - installOk = False - # block install if versions are the same - elif package.PackageVersionNumber == package.InstalledVersionNumber: - installOk = False - - if installOk or oneTimeInstall: + if installOk: packageOperationOk = False actionMessage = "installing " + packageName + " ..." PushAction ( command='install' + ':' + packageName, source='AUTO' ) @@ -3875,41 +3853,22 @@ def mainLoop (): # wait for two complete passes with nothing happening # before triggering reboot, GUI restart or initializing PackageManager Settings + # these actions are all handled in main () after mainLoop () exits if noActionCount >= 2: - if SystemReboot: - statusMessage = "rebooting ..." - # exit the main loop - mainloop.quit() - return False - elif InitializePackageManager: - statusMessage = "initializing and restarting PackageManager ..." - # exit the main loop - mainloop.quit() - return False - elif RestartPackageManager: - DbusIf.UpdateStatus ( "restarting PackageManager ...", where='PmStatus' ) - DbusIf.SetEditStatus ("") - DbusIf.AcknowledgeGuiEditAction ('') - # exit the main loop - mainloop.quit() - return False - elif GuiRestart: - DbusIf.UpdateStatus ( "restarting GUI and Package Manager...", where='PmStatus' ) - DbusIf.SetEditStatus ("") - DbusIf.AcknowledgeGuiEditAction ('') - # exit the main loop + if SystemReboot or InitializePackageManager or GuiRestart or RestartPackageManager: + # already exiting - include pending operations + if systemAction == REBOOT_NEEDED: + SystemReboot = True + elif systemAction == GUI_RESTART_NEEDED: + GuiRestart = True mainloop.quit() return False - if statusMessage != "": - DbusIf.UpdateStatus ( statusMessage, where='PmStatus' ) - elif actionMessage != "": + if actionMessage != "": DbusIf.UpdateStatus ( actionMessage, where='PmStatus' ) else: if emptyPackageList: idleMessage = "no active packages" - elif reinstallModsRunning: - idleMessage = "waiting for boot reinstall to complete" elif bootInstall: idleMessage = "reinstalling packages after firmware update" elif WaitForGitHubVersions: @@ -3965,6 +3924,27 @@ def directUninstall (packageName): GuiRestart = True +# signal handler for TERM and CONT +# this is needed to allow pending operations to finish before PackageManager exits +# TERM sets RestartPackageManager which causes mainLoop to exit and therefor main to complete +# TERM, then CONT is issued by supervise when shutting down the service +# CONT handler differentiates a restart vs service down for logging purposes + +def setPmRestart (signal, frame): + global RestartPackageManager + + RestartPackageManager = True + +def shutdownPmRestart (signal, frame): + global RestartPackageManager + global ShutdownPackageManager + if RestartPackageManager: + ShutdownPackageManager = True + +signal.signal (signal.SIGTERM, setPmRestart) +signal.signal (signal.SIGCONT, shutdownPmRestart) + + # main # # ######## code begins here @@ -3978,12 +3958,14 @@ def main(): global GuiRestart # initialized in main, set in PushAction, InstallPackage, used in mainloop global InitializePackageManager # initialized in main, set in PushAction, used in mainloop global RestartPackageManager # initialized in main, set in PushAction, used in mainloop + global ShutdownPackageManager global SetupHelperUninstall global WaitForGitHubVersions # initialized in main, set in UpdateGitHubVersion used in mainLoop SystemReboot = False GuiRestart = False InitializePackageManager = False RestartPackageManager = False + ShutdownPackageManager = False SetupHelperUninstall = False # set logging level to include info level entries @@ -4162,6 +4144,35 @@ def main(): #### this section of code runs only after the mainloop quits (LOCK / UNLOCK no longer necessary) + # output final prompts to GUI and log + DbusIf.DbusService['/ActionNeeded'] = "" + DbusIf.SetEditStatus ("") + DbusIf.AcknowledgeGuiEditAction ('') + message = "" + if MediaScan.AutoUninstall: + message = "UNINSTALLING ALL PACKAGES & REBOOTING ..." + logging.warning (">>>> UNINSTALLING ALL PACKAGES & REBOOTING...") + elif SetupHelperUninstall: + message = "UNINSTALLING SetupHelper ..." + logging.critical (">>>> UNINSTALLING SetupHelper ...") + elif InitializePackageManager: + if SystemReboot: + message = "initializing and REBOOTING ..." + logging.warning (">>>> initializing PackageManager and REBOOTING SYSTEM") + else: + logging.warning (">>>> initializing PackageManager ...") + message = "initializing and restarting PackageManager ..." + elif SystemReboot: + message = "REBOOTING SYSTEM ..." + logging.warning (">>>> REBOOTING SYSTEM") + elif GuiRestart: + message = "restarting GUI and Package Manager..." + elif ShutdownPackageManager: + message = "shutting down PackageManager ..." + elif RestartPackageManager: + message = "restarting PackageManager ..." + DbusIf.UpdateStatus ( message=message, where='PmStatus' ) + DbusIf.UpdateStatus ( message=message, where='Editor' ) # stop threads, remove service from dbus logging.warning ("stopping threads") @@ -4172,11 +4183,11 @@ def main(): MediaScan.StopThread () try: - UpdateGitHubVersion.join (timeout=5.0) - DownloadGitHub.join (timeout=5.0) - InstallPackages.join (timeout=5.0) - AddRemove.join (timeout=5.0) - MediaScan.join (timeout=5.0) + UpdateGitHubVersion.join (timeout=1.0) + DownloadGitHub.join (timeout=1.0) + InstallPackages.join (timeout=1.0) + AddRemove.join (timeout=1.0) + MediaScan.join (timeout=1.0) except: logging.critical ("attempt to join threads failed - one or more threads failed to exit") pass @@ -4190,28 +4201,12 @@ def main(): # auto uninstall triggered by AUTO_UNINSTALL_PACKAGES flag file on removable media # notify before DbusServicces is removed if MediaScan.AutoUninstall: - DbusIf.UpdateStatus ( message="UNINSTALLING ALL PACKAGES & REBOOTING ...", where='PmStatus') - DbusIf.UpdateStatus ( message="UNINSTALLING ALL PACKAGES & REBOOTING ...", where='Editor' ) - DbusIf.DbusService['/ActionNeeded'] = "" - logging.warning (">>>> UNINSTALLING ALL PACKAGES & REBOOTING...") SystemReboot = True - # uninstall all packages EXCEPT SetupHelper which is done later for path in os.listdir ("/data"): packageDir = "/data/" + path - if not os.path.isdir (packageDir): - continue - directUninstall (path) - elif SetupHelperUninstall: - DbusIf.UpdateStatus ( "UNINSTALLING SetupHelper ...", where='PmStatus' ) - DbusIf.UpdateStatus ( "UNINSTALLING SetupHelper ...", where='Editor' ) - DbusIf.DbusService['/ActionNeeded'] = "" - logging.critical (">>>> UNINSTALLING SetupHelper ...") - elif SystemReboot: - DbusIf.UpdateStatus ( message="REBOOTING SYSTEM ...", where='PmStatus') - DbusIf.UpdateStatus ( message="REBOOTING SYSTEM ...", where='Editor' ) - DbusIf.DbusService['/ActionNeeded'] = "" - logging.warning (">>>> REBOOTING SYSTEM") + if os.path.isdir (packageDir): + directUninstall (path) # remaining tasks are handled in packageManagerEnd.sh because # SetupHelper uninstall needs to be done after PackageManager.py exists @@ -4232,15 +4227,12 @@ def main(): except: logging.critical ("packageManagerEnd.sh failed") - # delay to leave reboot message up on status lines for a few seconds - # and to provide sufficient time for packageManagerEnd.sh to start up - time.sleep (5.0) + # delay to provide time for packageManagerEnd.sh to start up + time.sleep (0.1) DbusIf.RemoveDbusService () logging.warning (">>>> PackageManager exiting") - # program exits here - # Always run our main loop so we can process updates main() diff --git a/changes b/changes index b9535fd..bf0cecd 100644 --- a/changes +++ b/changes @@ -26,7 +26,11 @@ v8.0: (beta) moved SetupHelper logging to /var/log/PackageManager/current fixed: uninstall of patched files failed added manual recheck for errors discovered in setup script prechecks - + move patched files to /etc/venus so it tracks the selected root fs and is cleared + when the firmware is updated + PackageManager completes pending operations before exiting + fixed: patch not working + v7.18: fixed: only first service is uninstalled diff --git a/packageManagerEnd.sh b/packageManagerEnd.sh index a84e859..51b9913 100755 --- a/packageManagerEnd.sh +++ b/packageManagerEnd.sh @@ -62,7 +62,7 @@ while [ $# -gt 0 ]; do shift done -# allow PackageManager exit before doing anything +# wait for PackageManager to exit before doing anything if $shUninstall || $reboot ; then service="/service/PackageManager" # insure the PackageManager service doesn't restart when it exits diff --git a/reinstallMods b/reinstallMods index 9dcdc22..1a3dac4 100755 --- a/reinstallMods +++ b/reinstallMods @@ -1,80 +1,39 @@ #!/bin/sh -# reinstalMods will reinstall SetupHelper if a Venus OS update removes it -# -# other packages will then be reinstalled by PackageManager +# this script is called from /data/rcS.local during system boot +# it checks to see if SetupHelper and the PackageManager service +# are installed and if not, will install the PackageManager service # +# the REINSTALL_PACKAGES flag file is then set so that when +# PackageManger runs, it will do boot-time reinstall checks for all packages scriptDir="$( cd "$(dirname $0)" >/dev/null 2>&1 ; /bin/pwd -P )" -packageBase="$( dirname $scriptDir )" helperResourcesDir="$scriptDir/HelperResources" source "$helperResourcesDir/EssentialResources" source "$helperResourcesDir/LogHandler" +source "$helperResourcesDir/ServiceResources" # disable outputting log messages to console -runningAtBoot=true +logToConsole=false logMessage "reinstallMods starting" -# prevent PackageManager from conflicting with this script -rm -f "/etc/venus/REINSTALL_PACKAGES" - -rebootNeeded=false if [ -f "$setupOptionsDir/DO_NOT_AUTO_INSTALL" ]; then logMessage "CRITICAL: SetupHelper was manually uninstalled therefore it was not reinstalled" - logMessage " other packages will NOT BE REINSTALLED either !!!!!" - exit -fi - -# SetupHelper not installed, attempt to do so -if ! [ -f "$installedVersionFile" ]; then - command="$scriptDir/setup" - if [ ! -f $command ] ; then - logMessage "ERROR: SetupHelper setup script not found - can't reinstall" - exit - fi - - # flag to prevent PackageManager from conflicting with installing SetupHelper here - # probably will never happen but just in case - touch "/etc/venus/REINSTALL_MODS_RUNNING" - - # run setup script - $command reinstall auto deferReboot - returnCode=$? + logMessage " other packages will NOT BE REINSTALLED either !" + rm -f "/etc/venus/REINSTALL_PACKAGES" - rm -f "/etc/venus/REINSTALL_MODS_RUNNING" - - case $returnCode in - $EXIT_SUCCESS ) - ;; - $EXIT_REBOOT ) - rebootNeeded=true - ;; - $EXIT_NO_GUI_V1 ) - logMessage "ERROR: SetupHelper install failed - no GUI v1 installed" - exit - ;; - $EXIT_ROOT_FULL ) - logMessage "ERROR: SetupHelper install failed - no room in root partition" - exit - ;; - *) - logMessage "ERROR: SetupHelper install failed - reason $returnCode" - exit - ;; - esac - logMessage "SetupHelper installed" -else - logMessage "SetupHelper already installed" -fi - -# reboot now if signaled from setup script -if $rebootNeeded ; then - logMessage "rebooting ..." - reboot +# install PackageManager service if not yet installed else - # tell PackageManager to reinstall the remaining packages" - logMessage "PackageManager will reinstall remaining packages" + # install PackageManager service if not yet installed + if ! [ -e "$serviceDir/PackageManager" ]; then + logMessage "installing PackageManager service - PackageManager will reinstall all packages" + installService PackageManager + fi + # notify PackageManager that it needs to check all packages for possible reinstall + # flag file is cleared in PackageManager when all install checks have been made touch "/etc/venus/REINSTALL_PACKAGES" fi +logMessage "reinstallMods finished" + diff --git a/reinstallMods copy b/reinstallMods copy new file mode 100755 index 0000000..4b64220 --- /dev/null +++ b/reinstallMods copy @@ -0,0 +1,121 @@ +#!/bin/sh + +# reinstalMods will reinstall SetupHelper if a Venus OS update removes it +# +# other packages will then be reinstalled by PackageManager +# + +# this script is called from /data/rcS.local during system boot +# it checks to see if SetupHelper and the PackageManager service +# are installed and if not, will install the PackageManager service +# +# the REINSTALL_PACKAGES flag file is then set so that when +# PackageManger runs, it will do boot-time reinstall checks of all packages + +scriptDir="$( cd "$(dirname $0)" >/dev/null 2>&1 ; /bin/pwd -P )" +helperResourcesDir="$scriptDir/HelperResources" +source "$helperResourcesDir/EssentialResources" +source "$helperResourcesDir/LogHandler" + +# disable outputting log messages to console +runningAtBoot=true + +logMessage "reinstallMods starting" + + + + + + + + + +command="$scriptDir/setup" + +installSetupHelper=true +# SetupHelper already installed, nothing to do +if [ -f "$installedVersionFile" ]; then + logMessage "SetupHelper already installed" + installSetupHelper=false +elif [ -f "$setupOptionsDir/DO_NOT_AUTO_INSTALL" ]; then + logMessage "CRITICAL: SetupHelper was manually uninstalled therefore it was not reinstalled" + logMessage " other packages will NOT BE REINSTALLED either !" + installSetupHelper=false +elif ! [ -f $command ] ; then + logMessage "ERROR: SetupHelper setup script not found - can't reinstall" + installSetupHelper=false +fi + +rebootNeeded=false + +if $installSetupHelper; then + # hold off PackageManager processing + # this is unlikely since SetupHelper isn't installed yet + touch "/etc/venus/REINSTALL_MODS_RUNNING" + waitReported=false + # wait until PM is no longer running + # or is waiting for this script to finish + while true ; do + if [ -n $( pgrep -f PackageManager.py ) ]; then + pmStatus=$( dbus -y com.victronenergy.packageManager /PmStatus GetValue ) + if [[ "$pmStatus" == *"boot reinstall"* ]]; then + if $waitReported ; then + logMessage "PackageManager waiting for reinstallMods to finish" + fi + break + elif ! $waitReported ; then + logMessage "waiting for PackageManager to complete operations" + waitReported=true + fi + else + if $waitReported ; then + logMessage "PackageManager no longer running" + fi + break + fi + sleep 2 + done + + # run setup script + installOtherPackages=false + $command reinstall auto deferReboot + returnCode=$? + + case $returnCode in + $EXIT_SUCCESS ) + installOtherPackages=true + ;; + $EXIT_REBOOT ) + installOtherPackages=true + rebootNeeded=true + ;; + $EXIT_NO_GUI_V1 ) + logMessage "ERROR: SetupHelper install failed - no GUI v1 installed" + ;; + $EXIT_ROOT_FULL ) + logMessage "ERROR: SetupHelper install failed - no room in root partition" + ;; + *) + logMessage "ERROR: SetupHelper install failed - reason $returnCode" + ;; + esac + + if $installOtherPackages ; then + logMessage "SetupHelper installed - PackageManager will reinstall remaining packages" + # flag file is cleared in PackageManager when all install checks have been made + touch "/etc/venus/REINSTALL_PACKAGES" + fi +fi + +# reboot now if signaled from setup script +# if rebooting, PackageManager processing will be held until +# this script runs on next boot and clears the flag below. +if $rebootNeeded ; then + logMessage "rebooting ..." + reboot +# allow PackageManager to continue processing +else + rm -f "/etc/venus/REINSTALL_MODS_RUNNING" + logMessage "reinstallMods finished" +fi + diff --git a/venus-data-UninstallPackages.tgz b/venus-data-UninstallPackages.tgz deleted file mode 100644 index c9598b9..0000000 Binary files a/venus-data-UninstallPackages.tgz and /dev/null differ diff --git a/venus-data.tgz b/venus-data.tgz deleted file mode 100644 index 73b799e..0000000 Binary files a/venus-data.tgz and /dev/null differ diff --git a/version b/version index 84b2f94..549dce5 100644 --- a/version +++ b/version @@ -1 +1 @@ -v8.0~33 +v8.0~35