Open CODeRUS opened 5 years ago
Thanks, this may resolve issue #33 as well.
Edit: No it won't, see https://github.com/sailfishos-patches/patchmanager/issues/33#issuecomment-926988815. Nevertheless, to implement this enhancement does make sense.
I'm sorry, I'd like to get some context on this.
Change proposed is to add a Conflicts=os-update.target
entry to /usr/lib/systemd/system/dbus-org.SfietKonstantin.patchmanager.service
, correct?
With the goal to not interfere with anything going on during a system update.
Qs:
[*] with definitions of reliable adapted to the reality of how SFOS/Jolla changes happen... ;)
As:
os-update.target
is active.os-update.target
, only a progress bar is shown while the RPMs are installed and finally the device switches (or reboots, I do not remember precisely) to the regular multiuser-with-UI target.
Mind that this method of upgrading SFOS cannot be used on community ports and "Sailfish Free / Trial" installations (i.e., SFOS installations without a paid license from Jolla) and there are many other ways of upgrading SFOS (the "zypper dance" / per zypper --dup
, per version dup
/ sfos-upgrade
, per pkcon upgrade-system
), which all do not boot / switch to a special target.
TL;DR: os-update.target
is a kind of "single user mode for upgrading SFOS", used when triggering an OS upgrade per Settings app.
c. Well, this mechanism exists at least since SFOS 1.1.x (when I joined the club). ssu re <version-to-upgrade-to>
while at the regular multiuser-with-UI target (the GUI upgrade likely does also set SSU to release mode, but only while at the os-update.target
). The Seamless Software Update (SSU) utility was originally developed by Nokia for the N900 and Jolla's documentation for it is extremely terse, but their MDM documentation shows that there is an "int deviceMode() - The repository configuration mode e.g. Release, RnD, Upgrade. See ssu docs for further info.", which seems to be query-able (but I have no idea, where the mentioned documentation may be and if there is a hook / callback to be triggered).
But you can dig deeper into SSU, its local infrastructure and how it is embedded into SFOS: https://github.com/search?q=org%3Asailfishos+ssuHTH If there are more questions, do ask, as the basics of this (local) low-level infrastructure is a field I was interested in for a long time (and hence have gathered some knowledge). But Andrey is likely an expert on this, because these things are part of his day job, AFAIK.
Thank you. Much clearer now.
@Olf0 Can you please test https://github.com/nephros/patchmanager/commit/d14a3ee9287fc48f48668d0978be92ee7edaef10 somehow? (Well, it's just the single line added :) ). I have confirmed adding the line doesn't affect restarting the service or rebooting, but I do not know how to test the actual "os-update.target exists" case.
Thank you. Much clearer now.
Well, then it is time for me to report the things I do not understand WRT @CODeRUS' original issue description, "Add os-update.target to conflicts of pm service": See below.
@Olf0 Can you please test nephros@d14a3ee somehow? (Well, it's just the single line added :) ). I have confirmed adding the line doesn't affect restarting the service or rebooting, but I do not know how to test the actual "os-update.target exists" case.
Just adding Conflicts=os-update.target
(to bin/patchmanager-daemon/systemd/dbus-org.SfietKonstantin.patchmanager.service
) is definitely too simple:
Conflicts=
does not include a (temporal) order and does not make much sense (or results in unexpected behaviour) without one. See https://www.freedesktop.org/software/systemd/man/systemd.unit.html#Conflicts=
Hence an After=os-update.target
or Before=os-update.target
should be added, but which? → See below."os-update.target"
reveals ... nothing.system-update.target
systemctl -l -t target --all
(or an equivalent systemctl -l -t target --all list-units
) does not show an os-update.target
!
Neither does a systemctl -l -t target --all list-unit-files
show anything indicating such a target!But at least re-reading systemd's man-page for "offline updates", specifically "Recommendation 4" convinces me that Before=os-update.target
is the better ordering statement.
Unfortunately system-update.target
is completely missing on systemd's man-page "bootup".
@CODeRUS, can you please provide some background on os-update.target
, how it works, where it can be found and analysed, and if simply adding Conflicts=os-update.target
and Before=os-update.target
to bin/patchmanager-daemon/systemd/dbus-org.SfietKonstantin.patchmanager.service
is what you had in mind.
Good hints on where to look for some more! I conclude that os-upgrade.target
was never meant literally, and it has always been about that systemd special.
I found that the packagekit package does contain some things related to OS-update:
For one there's a snippet of documentation on how to use the built-in feature: https://github.com/sailfishos/PackageKit/blob/master/docs/offline-updates.txt
And looking at the installed package on my device:
$ rpm -ql PackageKit | grep systemd
/usr/lib/systemd/system/packagekit-offline-update.service
/usr/lib/systemd/system/packagekit.service
/usr/lib/systemd/system/system-update.target.wants/packagekit-offline-update.service
So this: https://github.com/sailfishos/PackageKit/blob/master/data/packagekit-offline-update.service.in
Aaand there's also sailfish-upgrade-ui
which contains:
/usr/lib/systemd/system/osupdate-logging.service
/usr/lib/systemd/system/sailfish-upgrade-ui.service
/usr/lib/systemd/system/system-update-cleanup.service.d
/usr/lib/systemd/system/system-update-cleanup.service.d/os-update-running.conf
/usr/lib/systemd/system/system-update-pre.target.wants
/usr/lib/systemd/system/system-update-pre.target.wants/sailfish-upgrade-ui.service
/usr/libexec/sailfish-upgrade-ui
of which /usr/lib/systemd/system/system-update-cleanup.service.d/os-update-running.conf
contains the very interesting:
[Unit]
ConditionPathExists=!/tmp/os-update-running
with /usr/lib/systemd/system/sailfish-upgrade-ui.service
doing
ExecStartPre=-/bin/touch /tmp/os-update-running
ExecStopPost=-/bin/rm -f /tmp/os-update-running
So assuming that mechanism is used during OS updates, we should either
Conflicts=
(and Before=/After=
) one of those servicesConditionPathExists=!/tmp/os-update-running
It looks to me like just having that Condition is sufficient for the purpose of not starting if update is running.
And if I read the documentation you linked to correctly, we shouldn't have system-update.target
anywhere in the Unit definition, or else we're actually pulled as a dep in that target which is the opposite of what we want. In fact, we shouldn't even be started for system-update.target
so maybe this whole issue is actually moot -- unless our After=dbus.service
somehow pulls us into the dep graph during updates.
One more thing:
Our RPM installs or changes /etc/ld.so.preload
, which will of course be in effect always, even in upgrade mode. We could introduce a special service which Requires/Before=system-update-pre.target
which moves that file out of the way during updates, so failure to load the library doesn't kill the update process.
Or we could set NO_PM_PRELOAD as an environment variable, but I don't know how to set that globally for everything and only in system-update mode.
I conclude that
os-upgrade.target
was never meant literally, and it has always been about that systemd special.
… system-update.target
.
Yes, probably.
[...] of which
/usr/lib/systemd/system/system-update-cleanup.service.d/os-update-running.conf
contains the very interesting:[Unit] ConditionPathExists=!/tmp/os-update-running
Yes, now I remember to have seen this pattern a couple of times in systemd units created by Jolla.
with
/usr/lib/systemd/system/sailfish-upgrade-ui.service
doingExecStartPre=-/bin/touch /tmp/os-update-running ExecStopPost=-/bin/rm -f /tmp/os-update-running
Thanks, while I have become a little bit of a systemd expert due to analysing SailfishOS, I never took the effort to track that down.
So assuming that mechanism is used during OS updates, we should either
* `Conflicts=` (and `Before=/After=`) one of those services
IMO, rather not, but ...
* `ConditionPathExists=!/tmp/os-update-running`
exactly that.
It looks to me like just having that Condition is sufficient for the purpose of not starting if update is running.
Well, sort of: But a ConditionPathExists=!/tmp/os-update-running
will not stop our unit, when the condition becomes untrue, when it is already started.
While dependencies define when units are queued for starting, the Condition
and Assert
statements are evaluated, when a unit is started; but after that … it would take some research for me to see if anything can be done then.
I think the solution is not to have it enqueued too early.
And if I read the documentation you linked to correctly, we shouldn't have
system-update.target
anywhere in the Unit definition, or else we're actually pulled as a dep in that target which is the opposite of what we want.
No, I do not believe so.
But it is not necessary this way (with ConditionPathExists=!/tmp/os-update-running
), any way.
In fact, we shouldn't even be started for
system-update.target
so maybe this whole issue is actually moot -- unless ourAfter=dbus.service
somehow pulls us into the dep graph during updates.
Wrong conclusion: It is systemd and its automatisms make it complicated to comprehend! :stuck_out_tongue_winking_eye:
After=dbus.service
alone does nothing, it is just a directive for temporal ordering, if some dependency is defined.
Requires=dbus.service
is such a dependency.
But both are unnecessary, because Type=dbus
automatically sets both (Requires=dbus.service
and After=dbus.service
) any way, unless one sets DefaultDependencies=No
. OTOH, it does no harm to duplicate statements, i.e. to express explicitly, what systemd already does implicitly.
And AFAIR (look at the boot chart) the dbus.target
is reached before the units for the system-update.target
are started.
Mind that targets are just defined steps in the boot process to group units.
The systemd man-pages are basically well written (except for the never properly defined, but often used phrase "to pull in"), but one has to read the most important ones thoroughly to gather an overall understanding. The issue is to find "the most important ones", because that is nowhere documented and depends a bit on the use case.
TL;DR: Solely use ConditionPathExists=!/tmp/os-update-running
instead of the currently inserted Conflicts=os-update.target
.
But please also take a look at the other units, they shall not be activated when /tmp/os-update-running
exists, too.
P.S.: I will try to take a closer look at all these units some time, because at first sight a couple of things can and likely should be cleaned up. One issue with systemd is, that it often accepts superfluous and even contradicting statements silently, while trying to do the right thing (and even often succeeds with that), but sometimes such statements cause weird side effects. In general systemd is a topic for which I should be able to assist well with guidance, know-how (just having studied all man-pages at least 3 times, some 10+) and practical experience. It is just very tedious to read and reread these man-pages often, but they seem to be exhausting, i.e. nothing is not defined in them. Trying things out is even more tedious with systemd, because everything is somewhat complicated (command syntax etc.), but feasible.
One more thing:
Our RPM installs or changes
/etc/ld.so.preload
, which will of course be in effect always, even in upgrade mode.
But that behaves transparently (i.e., like its original version), when it loads things not on a whitelist, correct? Or does nothing call it, when the PM-unit is not active?
Sorry, I never looked those things up and have not yet thought about them.
"installs or changes" (i.e., which of both) is the first thing we should know for sure!
We could introduce a special service which
Requires/Before=system-update-pre.target
which moves that file out of the way during updates, so failure to load the library doesn't kill the update process.
Which "the library"? (Will ponder about that, some time.)
Or we could set NO_PM_PRELOAD as an environment variable, but I don't know how to set that globally for everything and only in system-update mode.
I do not think that is viable.
Add os-update.target to conflicts of pm service