RFC: adding shell scripting capabilities will bring PM2 to the next level

SYSTEM PM2 PATCHES, IMPLEMENTATION EXAMPLE

In applying this Patch Manager patch, two cases arises

case #1: cacerts_gps folder exists

[root@sfos ~]# ls -al /system/etc/security/cacerts_gps
isrgrootx1.pem -> /tmp/patchmanager/.../isrgrootx1.pem
roots.pem -> /tmp/patchmanager/.../roots.pem

case #2: cacerts_gps folder does not exist

[root@sfos ~]# ls -al /system/etc/security/cacerts_gps
cacerts_gps -> /tmp/patchmanager/system/etc/security/cacerts_gps

Both case are wrong because Patch Manager creates symlink where it was supposed to create dirs and files. In fact, symlinks are not the same story because some tools that operate on the filesystem required a specific option to follow symlinks. Like old versions of the tar and tar is one of the widely used tool for doing backups. The approach can be easily changed following these examples:

mkdir /tmp/test
cd /tmp/test
repourl="coderus.openrepos.net/media/documents"
tarball="robang74-x10ii-iii-agps-config-emea-0.2.0.tar.gz"
curl https://$repourl/$tarball | tar xvzf -

The following shell code will works with -p0 and with -p1 because the sed regex deal with /var, new/var and ./new/var indifferently and every relative filepath is converted into an absolute /filepath.

files=$(sed -ne "s,^+++ *\.*[^/]*\([^ ]*\).*,/\\1,p" unified_diff.patch)
grep -qe "^+++ */" unified_diff.patch || false
plvl=$?

Now it is time to do a backup, for future restore but remember that overlay tricks the old tar, therefore a recent tar or busybox tar is needed

busybox tar czf /$store_dirpath/patch-$project_name.tar.gz $files
echo "$files" > /$store_dirpath/patch-$project_name.list

This determines the uninstall procedure by defining a global non-parametric function

patch_uninstall() { 
	rm -f $(cat /$store_dirpath/patch-$project_name.list)
	busybox tar xzf /$store_dirpath/patch-$project_name.tar.gz -C /
}

This is useful before apply the diff patch in order to creates dir and files not symlinks, therefore the symlinks engine could be ignored

patch_apply() {
	ret=0
	for i in $files; do mkdir -p $(basename $i); touch $i; done
	if ! patch -d / $pagrs -p$plvl unified_diff.patch; then 
		patch_uninstall
		ret=$((1+$?))
	fi
	return $ret
}

Is a system patch? For example system_diff.patch instead of unified_diff.patch? No, then uninstall at shutdown time. But why uninstall a patch at shutdown time with the risk that shutdown procedure and the related uninstall procedure can be interupped by a user physical keys controlled switch off? Because the filesystem for UI patches is volatile by default? Also the root filesystem about the reboot? Anyway:

num=$(printf "%05d" $(ls -1 /$store_dirpath/[0-9]*.tgz | wc -l))
busybox tar czf /$store_dirpath/$num-$project_name_applied.tar.gz $files

At boot time, in something functionally equivalent to /etc/rc.local but after all system device has been mounted not just those in /etc/fstab but before every systemd service will start, will be inserted the restoring procedure which will applies all the patch in their correct sequence

files=$(ls -1 /$store_dirpath/[0-9]*_applied.tar.gz)
test "$files" = "" && exit 0
for i in $files; do busybox tar xzf $i -C /; done

That’s all, unless I forgot or overlooked something essential or important.


UPDATE #1

This seems promising to install system updates also for those that do not require a reboot:

   system-update.target, system-update-pre.target,
   system-update-cleanup.service
       A special target unit that is used for offline system
       updates.  systemd-system-update-generator(8) will redirect
       the boot process to this target if /system-update or
       /etc/system-update exists. For more information see
       systemd.offline-updates(7).

It seems a general solution that requires - by documentation - a reboot but the reboot is managed by the configuration and not automatically enforced. However, due to its specific nature and delicacy, it can be a better option to add a service ordering related to system-update.target or even better system-update-pre.target in such a way that the patches which might conflicts with package updates will be applied before making them fail, as expected to be, instead of being overwritten.

UPDATE #2

The Patch Manager patches can be applied and unapplied many times during a user session and this is great feature :heart_eyes:. However, changing the way in which the PM2 works some of them might not fall into this category.

For example DNS Alternative is delivered like a RPM and it proobably it is the best way to have it. Before, it was a RPM package a developer might develop a patch. In general the way of providing a change could be exemplified in three main passages:

  1. system patch → 2. optional RPM package → 3. default RPM package

Which also implies three different level of integration with SFOS: unsopported (community only), supported in terms of the repository consistency (community + Jolla) and Jolla supported (commercial support). Which three different level of SLA and QoS in terms of supporting the end-users.

IMHO, the main difference between a system patch and a RPM package is bringing into the system new binaries rather than modify the system configuration. In this second case, having a system patch seems more reasonable expecially for the end-users that can choose to reconfigure the system as they wish - in the same manner they are doing with UI.

While UI patches can requires an application retart and a fingerprinter reader patch is functionally identical to a patch for e.g. the Settings, those have impact to the network and rely on the installation of 3rd party package e.g. dnsmasq need a little more attention. In fact, restarting network by SailFish Utilities do not consider the case in which dnsmasq is installed and configured (to fix). For all the others, a reboot is almost necessary.

This brings us to the conclusion that there are three patches classes:

  1. those changes UI, app, stand-alone services level: easy to restart
  2. those changes complex services like network/d-bus: might or might not be restarted
  3. those changes the system at such level that a reboot is needed: restart useless

The #1 and the #3 are almost straightforward cases to deal with. The second is a matter of policy: dnsmasq is optional but supported by network restart because it is an important feature that users usually requires to enable. Or on the other side is a 3rd party unsupported services an then the end-user needs to reboot his/her smartphone.