Merge ~gunnarhj/ubuntu-release-upgrader:temp-font into ubuntu-release-upgrader:ubuntu/main

Proposed by Gunnar Hjalmarsson
Status: Rejected
Rejected by: Nick Rosbrook
Proposed branch: ~gunnarhj/ubuntu-release-upgrader:temp-font
Merge into: ubuntu-release-upgrader:ubuntu/main
Diff against target: 114 lines (+85/-0)
1 file modified
DistUpgrade/DistUpgradeQuirks.py (+85/-0)
Reviewer Review Type Date Requested Status
Nick Rosbrook Needs Fixing
Brian Murray Pending
Ubuntu Core Development Team Pending
Review via email: mp+451815@code.launchpad.net

Description of the change

This is my attempt at a fix. Please note that I don't know
- how to test it live
- how I would check whether the Gtk frontend is in use (as was mentioned in the bug report)

As regards the latter, possibly it's not so important, after all. For e.g. Kubuntu, where they normally rely on fontconfig also for the desktop, the code wouldn't really change the font.

To post a comment you must log in.
Revision history for this message
Brian Murray (brian-murray) wrote :

With regards to testing the changes if you build the package you'll have a dist-upgrader tarball. You can then copy that to a system which you want to upgrade, unpack it in /tmp/, and the run `sudo ./mantic` or something really close to that. ;-)

Revision history for this message
Gunnar Hjalmarsson (gunnarhj) wrote :

@Brian: If I unpack that file and do "cd 23.10.7" I see another tarball: mantic.tar.gz.

> and the run `sudo ./mantic` or something really close to that. ;-)

Not sure what you mean by that.

Anyway, I added one basic check as a prerequisite to run the code.

Revision history for this message
Nick Rosbrook (enr0n) wrote :

> @Brian: If I unpack that file and do "cd 23.10.7" I see another tarball:
> mantic.tar.gz.
>
> > and the run `sudo ./mantic` or something really close to that. ;-)
>
> Not sure what you mean by that.
>
> Anyway, I added one basic check as a prerequisite to run the code.

You need to unpack mantic.tar.gz as well. Then you can run the mantic script.

Revision history for this message
Nick Rosbrook (enr0n) wrote :

Please see inline comments.

review: Needs Fixing
Revision history for this message
Gunnar Hjalmarsson (gunnarhj) wrote :

Sorry Nick, I did a forced push before I saw that you had looked at the code, so I'm afraid the inline comments are gone. Did you possibly make any notes separately?

Anyway, after your hint I could test the thing, and at the first attempt I found that the functions were misplaced. That has been corrected now, and I could test an upgrade with the proposed code.

But unfortunately it does not work as intended, and the reason is that the two functions are run as root while they would need to be run with dropped privileges. And I have absolutely no idea how to achieve that in Python.

So if we want to make this approach work, some more skilled developer needs to take over it from here.

Revision history for this message
Gunnar Hjalmarsson (gunnarhj) wrote :

@Nick: As regards your comments, I see them now via the email notification.

As regards the first condition in _set_generic_font: If that is not true, there is probably no reason for the quirk anyway, since Gtk then won't grab any font value from there.

> Why write this to a temp file? Why not just use an attribute like `self._original_font` or something?

Well, blame my limited coding experience. I would need a detailed instruction. But since we have a bigger problem, I suggest that we postpone such changes.

Revision history for this message
Gunnar Hjalmarsson (gunnarhj) wrote :

I have struggled a bit more with this. Tried a quick and dirty way to drop privileges, but it proved to not be sufficient.

The trick is to make it both read from and write to the right dconf database ($HOME/.config/dconf/user), and when I only changed setresgid and setresuid, it still interacted with /root/.cache/dconf/user. Changing HOME made me come closer, but it insists to create $HOME/.cache/dconf/user instead of updating $HOME/.config/dconf/user.

So I'm stuck. Again.

Revision history for this message
Nick Rosbrook (enr0n) wrote :

> I have struggled a bit more with this. Tried a quick and dirty way to drop
> privileges, but it proved to not be sufficient.
>
> The trick is to make it both read from and write to the right dconf database
> ($HOME/.config/dconf/user), and when I only changed setresgid and setresuid,
> it still interacted with /root/.cache/dconf/user. Changing HOME made me come
> closer, but it insists to create $HOME/.cache/dconf/user instead of updating
> $HOME/.config/dconf/user.
>
> So I'm stuck. Again.

There is a function in DistUpgradeController.py that should work. I think you just need to do this in both of your functions:

try:
   self.controller._setNonRootEUID()
   // existing code
finally:
   os.seteuid(os.getuid())

Revision history for this message
Gunnar Hjalmarsson (gunnarhj) wrote :

Thanks! Will give that a try tomorrow.

fb2c30b... by Gunnar Hjalmarsson

Use generic font temporarily at upgrade

To avoid that the fonts in the UI gets unreadable during the upgrade,
we replace the explicitly set font with a 'generic' one determined by
fontconfig (in practice DejaVu or Noto for latin scripts).

Fixes: https://launchpad.net/bugs/2034986

Revision history for this message
Gunnar Hjalmarsson (gunnarhj) wrote :

Pushed a new variant. This one does not work either.

When I put the code in two separate scripts and run those as e.g. "sudo ./myscript.py" it works as intended, i.e. the value of the font-name dconf key is changed and changed back successfully. Setting HOME and DBUS_SESSION_BUS_ADDRESS like that is needed for it to work in those scripts.

But as part of dist-upgrader, dconf seems to not acknowledge the changed HOME and DBUS_SESSION_BUS_ADDRESS variables, and thus fails to change the font. These are the warning messages:

dconf-CRITICAL **: 01:20:02.427: unable to create directory '/root/.cache/dconf': Permission denied. dconf will not work properly.

dconf-WARNING **: 01:20:02.428: failed to commit changes to dconf: Failed to execute child process “dbus-launch” (No such file or directory)

I also tried to replace "os.setresuid(uid, uid, -1)" with "os.seteuid(uid)", even if that does not work with my separate scripts. But it didn't help as part of dist-upgrader either.

Revision history for this message
Brian Murray (brian-murray) wrote :

do-release-upgrade passes some environment variables when starting the upgrade process, that might bear investigating.

Revision history for this message
Gunnar Hjalmarsson (gunnarhj) wrote :

Well, I noticed that do-release-upgrade plays with DBUS_SESSION_BUS_ADDRESS for some reason, but when checking that variable with a print() statement before my attempt to set it, I found that it was empty.

bbfcca7... by Gunnar Hjalmarsson

dconf() in subprocess instead of Gio.Settings

073a00a... by Gunnar Hjalmarsson

Log warnings

7ba1b30... by Gunnar Hjalmarsson

Save once

My tests indicate that the code in _set_generic_font() is run multiple
times for some reason. This check prevents misbehavior caused by that.

Revision history for this message
Gunnar Hjalmarsson (gunnarhj) wrote (last edit ):

At last I think we have a proposal that works as intended.

As regards dconf ignoring the HOME and DBUS_SESSION_BUS_ADDRESS variables it struck me that subprocess spawns new processes and inherits the environment of the current process. So I switched back to using subprocess instead of Gio.Settings, and that confirmed the hypothesis. Now it interacts with the dconf database of the user.

Another observation is that the code in _set_generic_font() is run multiple times for some to me unknown reason. I have added a check to prevent misbehavior due to that. It may not be the most elegant code you can think of...

And @Nick: /tmp is still used for a few things. Some day I may be motivated to learn about Python classes, attributes etc., but I won't struggle with that in a context where testing is so cumbersome as is the case with ubuntu-release-upgrader. So if you (or somebody else) want to polish the code in that respect, please go ahead.

So is the font rendering issue handled now? Almost, but not quite. It changes back to the Ubuntu font in connection with showing the window where the question about rebooting is asked, and then the characters are messed up. But maybe we can live with that. It's still a huge improvement that it happens only so late.

Revision history for this message
Gunnar Hjalmarsson (gunnarhj) wrote :

On 2023-09-25 19:05, Gunnar Hjalmarsson wrote:
> It changes back to the Ubuntu font in connection with showing the
> window where the question about rebooting is asked, and then the
> fonts are messed up. But maybe we can live with that. It's still a
> huge improvement that it happens only so late.

Or can the _back_to_original_font() call be postponed a little further still?

Revision history for this message
Nick Rosbrook (enr0n) wrote :

Thanks for all of your effort on this, Gunnar. I am going to test out your code now and see if I can help clean it up.

Revision history for this message
Nick Rosbrook (enr0n) wrote :

Gunnar - I have extended your first commit in https://code.launchpad.net/~enr0n/ubuntu-release-upgrader/+git/ubuntu-release-upgrader/+merge/452304, so let's continue on that MP.

Unmerged commits

7ba1b30... by Gunnar Hjalmarsson

Save once

My tests indicate that the code in _set_generic_font() is run multiple
times for some reason. This check prevents misbehavior caused by that.

073a00a... by Gunnar Hjalmarsson

Log warnings

bbfcca7... by Gunnar Hjalmarsson

dconf() in subprocess instead of Gio.Settings

fb2c30b... by Gunnar Hjalmarsson

Use generic font temporarily at upgrade

To avoid that the fonts in the UI gets unreadable during the upgrade,
we replace the explicitly set font with a 'generic' one determined by
fontconfig (in practice DejaVu or Noto for latin scripts).

Fixes: https://launchpad.net/bugs/2034986

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/DistUpgrade/DistUpgradeQuirks.py b/DistUpgrade/DistUpgradeQuirks.py
2index 0bd1ef9..e5160e7 100644
3--- a/DistUpgrade/DistUpgradeQuirks.py
4+++ b/DistUpgrade/DistUpgradeQuirks.py
5@@ -25,6 +25,7 @@ import distro_info
6 import glob
7 import logging
8 import os
9+import pwd
10 import re
11 import hashlib
12 import subprocess
13@@ -157,6 +158,7 @@ class DistUpgradeQuirks(object):
14 # individual quirks handler when the dpkg run is finished ---------
15 def PostCleanup(self):
16 " run after cleanup "
17+ self._back_to_original_font()
18 logging.debug("running Quirks.PostCleanup")
19
20 # run right before the first packages get installed
21@@ -168,6 +170,7 @@ class DistUpgradeQuirks(object):
22 self._killKBluetooth()
23 self._pokeScreensaver()
24 self._stopDocvertConverter()
25+ self._set_generic_font()
26
27 # individual quirks handler that run *right before* the dist-upgrade
28 # is calculated in the cache
29@@ -1447,3 +1450,85 @@ class DistUpgradeQuirks(object):
30 except IOError as exc:
31 logging.error("unable to write new boot config to %s: %s; %s",
32 boot_config_filename, exc, failure_action)
33+
34+ def _set_generic_font(self):
35+ """ Due to changes to the Ubuntu font we enable a generic font
36+ (in practice DejaVu or Noto) during the upgrade.
37+ See https://launchpad.net/bugs/2034986
38+ """
39+ uid = int(os.getenv("SUDO_UID") or os.getenv("PKEXEC_UID"))
40+ os.setresuid(uid, uid, -1)
41+ os.environ["HOME"] = pwd.getpwuid(uid).pw_dir
42+ os.environ["DBUS_SESSION_BUS_ADDRESS"] = 'unix:path=/run/user/'+str(uid)+'/bus'
43+
44+ # set cleanup flag
45+ if not os.path.isdir(os.getenv('HOME')+'/.cache/dconf'):
46+ f = open('/tmp/cleandconfcacheyes', 'w')
47+ f.close()
48+
49+ size = '11'
50+
51+ # try to grab user set font if any
52+ userfont = subprocess.run(['dconf', 'read', '/org/gnome/desktop/interface/font-name'],
53+ capture_output=True, text=True).stdout.rstrip()
54+ if userfont and not os.path.isfile('/tmp/nomorewriting'):
55+ f = open('/tmp/orig_user_font.txt', 'w')
56+ f.write(userfont)
57+ f.close()
58+ m = re.search(r'\d+', userfont)
59+ if m:
60+ size = m.group()
61+
62+ if not os.path.isfile('/tmp/nomorewriting'):
63+ res = subprocess.run(['dconf', 'write', '/org/gnome/desktop/interface/font-name',
64+ "'Sans "+size+"'"], capture_output=True, text=True)
65+ if res.returncode != 0:
66+ logging.warning(res)
67+
68+ # done with writing
69+ f = open('/tmp/nomorewriting', 'w')
70+ f.close()
71+
72+ del os.environ["DBUS_SESSION_BUS_ADDRESS"]
73+ os.environ["HOME"] = '/root'
74+ os.setresuid(0, 0, -1)
75+
76+ def _back_to_original_font(self):
77+ """ This funcion resets it to the user's original font after
78+ the temporary change in _set_generic_font().
79+ """
80+ uid = int(os.getenv("SUDO_UID") or os.getenv("PKEXEC_UID"))
81+ os.setresuid(uid, uid, -1)
82+ os.environ["HOME"] = pwd.getpwuid(uid).pw_dir
83+ os.environ["DBUS_SESSION_BUS_ADDRESS"] = 'unix:path=/run/user/'+str(uid)+'/bus'
84+
85+ orig_font = '/tmp/orig_user_font.txt'
86+ if os.path.isfile(orig_font):
87+ f = open(orig_font, 'r')
88+ userfont = f.read()
89+ f.close()
90+ os.remove(orig_font)
91+ res = subprocess.run(['dconf', 'write', '/org/gnome/desktop/interface/font-name',
92+ userfont], capture_output=True, text=True)
93+ if res.returncode != 0:
94+ logging.warning(res)
95+ else:
96+ res = subprocess.run(['dconf', 'reset', '/org/gnome/desktop/interface/font-name'],
97+ capture_output=True, text=True)
98+ if res.returncode != 0:
99+ logging.warning(res)
100+ if os.path.isfile('/tmp/nomorewriting'):
101+ os.remove('/tmp/nomorewriting')
102+
103+ # try to clean up in HOME
104+ if os.path.isfile('/tmp/cleandconfcacheyes'):
105+ try:
106+ os.remove(os.getenv('HOME')+'/.cache/dconf/user')
107+ os.rmdir(os.getenv('HOME')+'/.cache/dconf')
108+ except Exception:
109+ pass
110+ os.remove('/tmp/cleandconfcacheyes')
111+
112+ del os.environ["DBUS_SESSION_BUS_ADDRESS"]
113+ os.environ["HOME"] = '/root'
114+ os.setresuid(0, 0, -1)

Subscribers

People subscribed via source and target branches