[Bug 1953135] [NEW] Incorrect symlink checking => Your python3 install is corrupted.

Nico Schlömer 1953135 at bugs.launchpad.net
Fri Dec 3 09:23:47 UTC 2021


Public bug reported:

This is related to https://bugs.launchpad.net/bugs/1825655.

When trying to upgrade to jammy, I got
```
Your python3 install is corrupted. Please fix the '/usr/bin/python3' 
symlink. 
```

Digging into the matter, it comes down to this symlink checker in `DistUpgradeController.py`:
```
import logging
from configparser import ConfigParser
import os
from configparser import NoOptionError

logging.basicConfig(filename='example.log', encoding='utf-8',
level=logging.DEBUG)

def _pythonSymlinkCheck():
    """ sanity check that /usr/bin/python3 points to the default
        python version. Users tend to modify this symlink, which
        breaks stuff in obscure ways (Ubuntu #75557).
    """
    logging.debug("_pythonSymlinkCheck run")
    binaries_and_dirnames = [("python3", "python3")]
    for binary, dirname in binaries_and_dirnames:
        debian_defaults = '/usr/share/%s/debian_defaults' % dirname
        if os.path.exists(debian_defaults):
            config = ConfigParser()
            with open(debian_defaults) as f:
                config.read_file(f)
            try:
                expected_default = config.get('DEFAULT', 'default-version')
            except NoOptionError:
                logging.debug("no default version for %s found in '%s'" %
                              (binary, config))
                return False
            try:
                fs_default_version = os.readlink('/usr/bin/%s' % binary)
            except OSError as e:
                logging.error("os.readlink failed (%s)" % e)
                return False
            if not fs_default_version in (expected_default, os.path.join('/usr/bin', expected_default)):
                logging.debug("%s symlink points to: '%s', but expected is '%s' or '%s'" %
                              (binary, fs_default_version, expected_default, os.path.join('/usr/bin', expected_default)))
                return False
    return True


res = _pythonSymlinkCheck()
print(res)
```
Running it indeed returns `False` on my machine. The log gives the reason:
```
DEBUG:root:_pythonSymlinkCheck run
DEBUG:root:python3 symlink points to: '/etc/alternatives/python3', but expected is 'python3.9' or '/usr/bin/python3.9'
```
The symlink is not followed through.

Instead of `os.readlink`, you want to use `os.path.realpath`.

** Affects: ubuntu-release-upgrader (Ubuntu)
     Importance: Undecided
         Status: New

-- 
You received this bug notification because you are a member of Ubuntu
Foundations Bugs, which is subscribed to ubuntu-release-upgrader in
Ubuntu.
https://bugs.launchpad.net/bugs/1953135

Title:
  Incorrect symlink checking => Your python3 install is corrupted.

Status in ubuntu-release-upgrader package in Ubuntu:
  New

Bug description:
  This is related to https://bugs.launchpad.net/bugs/1825655.

  When trying to upgrade to jammy, I got
  ```
  Your python3 install is corrupted. Please fix the '/usr/bin/python3' 
  symlink. 
  ```

  Digging into the matter, it comes down to this symlink checker in `DistUpgradeController.py`:
  ```
  import logging
  from configparser import ConfigParser
  import os
  from configparser import NoOptionError

  logging.basicConfig(filename='example.log', encoding='utf-8',
  level=logging.DEBUG)

  def _pythonSymlinkCheck():
      """ sanity check that /usr/bin/python3 points to the default
          python version. Users tend to modify this symlink, which
          breaks stuff in obscure ways (Ubuntu #75557).
      """
      logging.debug("_pythonSymlinkCheck run")
      binaries_and_dirnames = [("python3", "python3")]
      for binary, dirname in binaries_and_dirnames:
          debian_defaults = '/usr/share/%s/debian_defaults' % dirname
          if os.path.exists(debian_defaults):
              config = ConfigParser()
              with open(debian_defaults) as f:
                  config.read_file(f)
              try:
                  expected_default = config.get('DEFAULT', 'default-version')
              except NoOptionError:
                  logging.debug("no default version for %s found in '%s'" %
                                (binary, config))
                  return False
              try:
                  fs_default_version = os.readlink('/usr/bin/%s' % binary)
              except OSError as e:
                  logging.error("os.readlink failed (%s)" % e)
                  return False
              if not fs_default_version in (expected_default, os.path.join('/usr/bin', expected_default)):
                  logging.debug("%s symlink points to: '%s', but expected is '%s' or '%s'" %
                                (binary, fs_default_version, expected_default, os.path.join('/usr/bin', expected_default)))
                  return False
      return True

  
  res = _pythonSymlinkCheck()
  print(res)
  ```
  Running it indeed returns `False` on my machine. The log gives the reason:
  ```
  DEBUG:root:_pythonSymlinkCheck run
  DEBUG:root:python3 symlink points to: '/etc/alternatives/python3', but expected is 'python3.9' or '/usr/bin/python3.9'
  ```
  The symlink is not followed through.

  Instead of `os.readlink`, you want to use `os.path.realpath`.

To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/ubuntu-release-upgrader/+bug/1953135/+subscriptions




More information about the foundations-bugs mailing list