diff --git a/.gitignore b/.gitignore
index c032d6af..c3545c78 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
build/
*.pro.*
src/bin/patchmanager-daemon/adaptor.*
+src/bin/patchmanager-daemon/patchmanager
*.rpm
*.so*
diff --git a/rpm/patchmanager.spec b/rpm/patchmanager.spec
index 6c790869..47924aa4 100644
--- a/rpm/patchmanager.spec
+++ b/rpm/patchmanager.spec
@@ -16,6 +16,8 @@ URL: https://github.com/sailfishos-patches/patchmanager
Source0: %{name}-%{version}.tar.bz2
Requires: unzip
Requires: patch
+Requires: grep
+Requires: sed
Requires: sailfish-version >= 3.4.0
BuildRequires: pkgconfig(Qt5Core)
BuildRequires: pkgconfig(Qt5DBus)
@@ -168,6 +170,7 @@ systemctl-user daemon-reload
%{_userunitdir}/lipstick.service.wants/lipstick-patchmanager.service
%{_libdir}/libpreload%{name}.so
%{_sysconfdir}/firejail/whitelist-common-%{name}.local
+%config(noreplace) %{_sysconfdir}/%{name}/manglelist.conf
%attr(0755,root,root) %{_libexecdir}/pm_apply
%attr(0755,root,root) %{_libexecdir}/pm_unapply
diff --git a/src/bin/patchmanager-daemon/dbus/org.SfietKonstantin.patchmanager.xml b/src/bin/patchmanager-daemon/dbus/org.SfietKonstantin.patchmanager.xml
index 22fd0eed..5214172d 100644
--- a/src/bin/patchmanager-daemon/dbus/org.SfietKonstantin.patchmanager.xml
+++ b/src/bin/patchmanager-daemon/dbus/org.SfietKonstantin.patchmanager.xml
@@ -59,6 +59,9 @@
+
+
+
diff --git a/src/bin/patchmanager-daemon/patchmanager b/src/bin/patchmanager-daemon/patchmanager
new file mode 100755
index 00000000..e73549a0
Binary files /dev/null and b/src/bin/patchmanager-daemon/patchmanager differ
diff --git a/src/bin/patchmanager-daemon/patchmanagerobject.cpp b/src/bin/patchmanager-daemon/patchmanagerobject.cpp
index 24609db7..6afdf021 100644
--- a/src/bin/patchmanager-daemon/patchmanagerobject.cpp
+++ b/src/bin/patchmanager-daemon/patchmanagerobject.cpp
@@ -86,7 +86,7 @@ static const QString PATCHES_WORK_DIR_PREFIX = QStringLiteral("/tmp/patchmanager
static const QString PATCHES_WORK_DIR = QStringLiteral("%1/%2").arg(PATCHES_WORK_DIR_PREFIX, "work");
static const QString PATCHES_ADDITIONAL_DIR = QStringLiteral("%1/%2").arg(PATCHES_WORK_DIR_PREFIX, "patches");
static const QString PATCH_FILE = QStringLiteral("patch.json");
-
+static const QString MANGLE_CONFIG_FILE = QStringLiteral("/etc/patchmanager/manglelist.conf");
static const QString NAME_KEY = QStringLiteral("name");
static const QString DESCRIPTION_KEY = QStringLiteral("description");
static const QString CATEGORY_KEY = QStringLiteral("category");
@@ -288,6 +288,17 @@ void PatchManagerObject::setAppliedPatches(const QSet &patches)
putSettings(QStringLiteral("applied"), QStringList(patches.toList()));
}
+QStringList PatchManagerObject::getMangleCandidates()
+{
+ if (m_mangleCandidates.empty()) {
+ qDebug() << Q_FUNC_INFO;
+ auto mangleCandidates = QSettings(MANGLE_CONFIG_FILE, QSettings::IniFormat).value("MANGLE_CANDIDATES", "").toString();
+ m_mangleCandidates = mangleCandidates.split(' ', QString::SplitBehavior::SkipEmptyParts);
+ qDebug() << "Loaded mangle candidates:" << m_mangleCandidates;
+ }
+ return m_mangleCandidates;
+}
+
void PatchManagerObject::getVersion()
{
qDebug() << Q_FUNC_INFO;
@@ -1157,6 +1168,10 @@ bool PatchManagerObject::putSettings(const QString &name, const QVariant &value)
QVariant old = m_settings->value(key);
if (old != value) {
m_settings->setValue(key ,value);
+ if (name == QStringLiteral("bitnessMangle")) {
+ qDebug() << Q_FUNC_INFO << "Changing bitness mangle refreshes patch list";
+ refreshPatchList();
+ }
return true;
}
return false;
@@ -1569,6 +1584,18 @@ void PatchManagerObject::doRefreshPatchList()
{
qDebug() << Q_FUNC_INFO;
+ // Create mangling replacement tokens.
+ QStringList toManglePaths{}, mangledPaths{};
+ if(getSettings(QStringLiteral("bitnessMangle"), false).toBool()) {
+ toManglePaths = getMangleCandidates();
+ mangledPaths = getMangleCandidates().replaceInStrings("/usr/lib/", "/usr/lib64/");
+ if (Q_PROCESSOR_WORDSIZE == 4) { // 32 bit
+ std::swap(toManglePaths, mangledPaths);
+ }
+ }
+ qDebug() << Q_FUNC_INFO << "toManglePaths" << toManglePaths;
+ qDebug() << Q_FUNC_INFO << "mangledPaths" << mangledPaths;
+
// load applied patches
m_appliedPatches = getAppliedPatches();
@@ -1590,6 +1617,14 @@ void PatchManagerObject::doRefreshPatchList()
if (line.startsWith(QByteArrayLiteral("+++ "))) {
const QString toPatch = QString::fromLatin1(line.split(' ')[1].split('\t')[0].split('\n')[0]);
QString path = toPatch;
+
+ for (int i = 0; i < toManglePaths.size(); i++) {
+ if (path.startsWith(toManglePaths[i])) {
+ qDebug() << Q_FUNC_INFO << "Editing path: " << path;
+ path.replace(toManglePaths[i], mangledPaths[i]);
+ }
+ }
+
while (!QFileInfo::exists(path) && path.count('/') > 1) {
path = path.mid(path.indexOf('/', 1));
}
@@ -1768,6 +1803,13 @@ bool PatchManagerObject::doPatch(const QString &patchName, bool apply, QString *
QStringList arguments;
arguments.append(patchName);
+ if (false == getSettings(QStringLiteral("bitnessMangle"), false).toBool()) {
+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+ qDebug() << Q_FUNC_INFO << "DISABLE_MANGLING=true";
+ env.insert("DISABLE_MANGLING", "true");
+ process.setProcessEnvironment(env);
+ }
+
process.setArguments(arguments);
qDebug() << Q_FUNC_INFO << "Starting:" << process.program() << process.arguments();
process.start();
diff --git a/src/bin/patchmanager-daemon/patchmanagerobject.h b/src/bin/patchmanager-daemon/patchmanagerobject.h
index 246e029e..3a67e1a1 100644
--- a/src/bin/patchmanager-daemon/patchmanagerobject.h
+++ b/src/bin/patchmanager-daemon/patchmanagerobject.h
@@ -40,6 +40,7 @@
#include
#include
#include
+#include
#include
#include
@@ -180,6 +181,8 @@ private slots:
void restartKeyboard();
void doRestartKeyboard();
+ QStringList getMangleCandidates();
+
private:
void restartService(const QString &serviceName);
@@ -237,6 +240,8 @@ private slots:
QString m_osRelease;
+ QStringList m_mangleCandidates;
+
PatchManagerAdaptor *m_adaptor = nullptr;
QNetworkAccessManager *m_nam = nullptr;
diff --git a/src/etc/etc.pro b/src/etc/etc.pro
index 6d59952f..009e1f98 100644
--- a/src/etc/etc.pro
+++ b/src/etc/etc.pro
@@ -1,6 +1,8 @@
TEMPLATE = aux
+manglelist.files = manglelist.conf
+manglelist.path = /etc/patchmanager
firejail.files = whitelist-common-patchmanager.local
firejail.path = /etc/firejail
-INSTALLS += firejail
+INSTALLS += firejail manglelist
diff --git a/src/etc/manglelist.conf b/src/etc/manglelist.conf
new file mode 100644
index 00000000..98c664c2
--- /dev/null
+++ b/src/etc/manglelist.conf
@@ -0,0 +1,9 @@
+# Settings file for 32/64 bit path mangling
+#
+# must conform to both shell format, and be parseable as QSettings(foo, QSettings::IniFormat)
+
+# list of candidate paths to attempt 32/64bit library path correction
+# used by patchmanager-daemon and pm_apply.sh
+# determined by a find /usr/lib -name "*.qml" and bug reports
+# the list is given in "32bit" format, i.e. no lib64
+MANGLE_CANDIDATES="/usr/lib/qt5/qml /usr/lib/jolla-mediaplayer /usr/lib/maliit/plugins"
diff --git a/src/qml/SettingsPage.qml b/src/qml/SettingsPage.qml
index 2fe78105..3b7dab32 100644
--- a/src/qml/SettingsPage.qml
+++ b/src/qml/SettingsPage.qml
@@ -31,6 +31,28 @@ Page {
onClicked: PatchManager.developerMode = !PatchManager.developerMode
automaticCheck: false
}
+
+ TextSwitch {
+ id: fixBitSwitch
+ text: qsTranslate("", "Fix patches made for 32-bit or 64-bit only")
+ description: qsTranslate("", "Automatically fix lib or lib64 for select paths shown below.")
+ checked: PatchManager.bitnessMangle
+ onClicked: PatchManager.bitnessMangle = !PatchManager.bitnessMangle
+ automaticCheck: false
+ }
+
+ TextArea {
+ // align to the right of TextSwitch indicator
+ anchors {
+ left: fixBitSwitch.left
+ leftMargin: fixBitSwitch.leftMargin + Theme.paddingLarge
+ }
+ color: Theme.secondaryColor
+ font.pixelSize: Theme.fontSizeSmall
+ readOnly: true
+ text: PatchManager.mangleCandidates.join("\n")
+ enabled: fixBitSwitch.checked
+ }
}
}
}
diff --git a/src/qml/patchmanager.cpp b/src/qml/patchmanager.cpp
index 0d17c325..82648984 100644
--- a/src/qml/patchmanager.cpp
+++ b/src/qml/patchmanager.cpp
@@ -197,6 +197,17 @@ bool PatchManager::applyOnBoot() const
return getSettingsSync(QStringLiteral("applyOnBoot"), false).toBool();
}
+QStringList PatchManager::mangleCandidates() const
+{
+ QDBusPendingReply reply = m_interface->getMangleCandidates();
+ reply.waitForFinished();
+ if (reply.isFinished()) {
+ qDebug() << Q_FUNC_INFO << "mangleCandidates() dbus replied:" << reply.value();
+ return reply.value();
+ }
+ return QStringList();
+}
+
void PatchManager::setApplyOnBoot(bool applyOnBoot)
{
if (putSettingsSync(QStringLiteral("applyOnBoot"), applyOnBoot)) {
@@ -204,6 +215,17 @@ void PatchManager::setApplyOnBoot(bool applyOnBoot)
}
}
+bool PatchManager::bitnessMangle() const
+{
+ return getSettingsSync(QStringLiteral("bitnessMangle"), false).toBool();
+}
+
+void PatchManager::setBitnessMangle(bool bitnessMangle)
+{
+ if (putSettingsSync(QStringLiteral("bitnessMangle"), bitnessMangle)) {
+ emit bitnessMangleChanged(bitnessMangle);
+ }
+}
PatchManagerModel *PatchManager::installedModel()
{
return m_installedModel;
diff --git a/src/qml/patchmanager.h b/src/qml/patchmanager.h
index 7b0f1a06..cfb8f715 100644
--- a/src/qml/patchmanager.h
+++ b/src/qml/patchmanager.h
@@ -66,6 +66,8 @@ class PatchManager: public QObject
Q_PROPERTY(QString serverMediaUrl READ serverMediaUrl CONSTANT)
Q_PROPERTY(bool developerMode READ developerMode WRITE setDeveloperMode NOTIFY developerModeChanged)
Q_PROPERTY(bool applyOnBoot READ applyOnBoot WRITE setApplyOnBoot NOTIFY applyOnBootChanged)
+ Q_PROPERTY(bool bitnessMangle READ bitnessMangle WRITE setBitnessMangle NOTIFY bitnessMangleChanged)
+ Q_PROPERTY(QStringList mangleCandidates READ mangleCandidates)
Q_PROPERTY(PatchManagerModel *installedModel READ installedModel CONSTANT)
Q_PROPERTY(QVariantMap updates READ getUpdates NOTIFY updatesChanged)
Q_PROPERTY(QStringList updatesNames READ getUpdatesNames NOTIFY updatesChanged)
@@ -83,6 +85,9 @@ class PatchManager: public QObject
void setDeveloperMode(bool developerMode);
bool applyOnBoot() const;
void setApplyOnBoot(bool applyOnBoot);
+ bool bitnessMangle() const;
+ void setBitnessMangle(bool bitnessMangle);
+ QStringList mangleCandidates() const;
PatchManagerModel *installedModel();
QString trCategory(const QString &category) const;
QVariantMap getUpdates() const;
@@ -156,6 +161,7 @@ public slots:
void easterReceived(const QString & easterText);
void developerModeChanged(bool developerMode);
void applyOnBootChanged(bool applyOnBoot);
+ void bitnessMangleChanged(bool bitnessMangle);
void updatesChanged();
void toggleServicesChanged(bool toggleServices);
void failureChanged(bool failed);
diff --git a/src/tools/pm_apply b/src/tools/pm_apply
index 55c47093..9a7bf548 100644
--- a/src/tools/pm_apply
+++ b/src/tools/pm_apply
@@ -15,9 +15,18 @@ PM_LOG_FILE="$PM_VAR_DIR/patchmanager.log"
PM_PATCH_BACKUP_ROOT_DIR="$PM_VAR_DIR/patches"
PM_PATCH_BACKUP_DIR="$PM_PATCH_BACKUP_ROOT_DIR/$1"
+SYS_BITNESS=$(/usr/bin/getconf LONG_BIT)
+
# Constants
PATCH_NAME="unified_diff.patch"
PATCH_PATH="$PATCH_DIR/$PATCH_NAME"
+PATCH_EDITED_NAME="unified_diff_${SYS_BITNESS}bit.patch"
+
+# list of candidate paths to attempt 32/64bit library path correction
+MANGLE_CANDIDATES=""
+if [ -z "$DISABLE_MANGLING" ] && [ -r "etc/patchmanager/manglelist.conf" ] ; then
+ source /etc/patchmanager/manglelist.conf
+fi
ROOT_DIR="/tmp/patchmanager"
@@ -36,6 +45,11 @@ log() {
echo "$@" | tee -a "$PM_LOG_FILE"
}
+log "Mangle candidates"
+log $MANGLE_CANDIDATES
+log "Disable mangling"
+log $DISABLE_MANGLING
+
failure() {
log
log "*** FAILED ***"
@@ -68,6 +82,69 @@ test_if_applied() {
fi
}
+# see issue #71: https://github.com/sailfishos-patches/patchmanager/issues/71
+mangle_libpath() {
+ if [ -f "$PATCH_PATH" ]; then
+ log
+ log "----------------------------------"
+ [ $SYS_BITNESS -eq 32 ] && log "Checking paths for 32->64bit conversion"
+ [ $SYS_BITNESS -eq 64 ] && log "Checking paths for 64->32bit conversion"
+ log "----------------------------------"
+ log
+
+ found=0
+ candidates="$MANGLE_CANDIDATES"
+ if [ $SYS_BITNESS -eq 32 ]; then
+ # first, convert the candidate list
+ # variable expansion ${foo/lib/lib64} would work on bash, but not POSIX/ash/busybox
+ candidates=$(printf '%s' "$MANGLE_CANDIDATES" | sed 's@/usr/lib/@/usr/lib64/@g' )
+ fi
+
+ for p in $candidates; do
+ totl_lines=$(grep -c "^+++" "$PATCH_PATH")
+ cand_lines=$(grep -c "^+++ $p" "$PATCH_PATH")
+ if [ $cand_lines -eq 0 ]; then
+ continue # nothing found, try next
+ else
+ found=$(( $found + $cand_lines ))
+ if [ $totl_lines -ne $cand_lines ]; then
+ # if there are several references in the patch file our mangling might do too much and cause the patch to fail.
+ log "WARNING: mixed patch, conversion might not work"
+ fi
+ fi
+
+ patch_edited_path="$PM_PATCH_BACKUP_DIR"/"$PATCH_EDITED_NAME"
+ log "found: ${p}, replacing libpath references"
+
+ mkdir -p "$PM_PATCH_BACKUP_DIR"
+ # prepare the pattern
+ pr=""
+ if [ $SYS_BITNESS -eq 32 ]; then
+ pr=$(printf '%s' "$p" | sed 's@/usr/lib64/@/usr/lib/@')
+ elif [ $SYS_BITNESS -eq 64 ]; then
+ pr=$(printf '%s' "$p" | sed 's@/usr/lib/@/usr/lib64/@')
+ fi
+ # doit
+ printf '#\n# patch converted to %sbit library paths from original by Patchmanager 3.1\n# Date: %s\n#\n' $SYS_BITNESS $(date -Iseconds) > "$patch_edited_path"
+ sed "s@^+++ $p@+++ $pr@;s@^--- $p@--- $pr@" "$PATCH_PATH" >> "$patch_edited_path"
+ if [ $? -ne 0 ]; then
+ failure
+ fi
+ log
+ log "OK, conversion produced: $patch_edited_path"
+ log
+ # set the patch to apply to the new one:
+ PATCH_PATH="$patch_edited_path"
+ return
+ done
+ if [ $found -eq 0 ]; then
+ log
+ log "OK, found nothing to convert"
+ log
+ fi
+ fi
+}
+
verify_text_patch() {
if [ -f "$PATCH_PATH" ]; then
log
@@ -157,6 +234,8 @@ fi
# The main function that controls all the magic stuff
#
+mangle_libpath
+
test_if_applied
verify_text_patch
diff --git a/src/tools/pm_unapply b/src/tools/pm_unapply
index 631777a3..d508dc0a 100644
--- a/src/tools/pm_unapply
+++ b/src/tools/pm_unapply
@@ -15,17 +15,23 @@ PM_LOG_FILE="$PM_VAR_DIR/patchmanager.log"
PM_PATCH_BACKUP_ROOT_DIR="$PM_VAR_DIR/patches"
PM_PATCH_BACKUP_DIR="$PM_PATCH_BACKUP_ROOT_DIR/$1"
+SYS_BITNESS=$(/usr/bin/getconf LONG_BIT)
+
# Constants
PATCH_NAME="unified_diff.patch"
PATCH_PATH="$PATCH_DIR/$PATCH_NAME"
PATCH_BACKUP="$PM_PATCH_BACKUP_DIR/$PATCH_NAME"
+PATCH_EDITED_NAME="unified_diff_${SYS_BITNESS}bit.patch"
+PATCH_EDITED_BACKUP="$PM_PATCH_BACKUP_DIR"/"$PATCH_EDITED_NAME"
ROOT_DIR="/tmp/patchmanager"
# Applications
PATCH_EXEC="/usr/bin/patch"
-if [ -f "$PATCH_BACKUP" ]; then
+if [ -f "$PATCH_EDITED_BACKUP" ]; then
+ PATCH_FILE="$PATCH_EDITED_BACKUP"
+elif [ -f "$PATCH_BACKUP" ]; then
PATCH_FILE="$PATCH_BACKUP"
else
PATCH_FILE="$PATCH_PATH"
@@ -111,6 +117,8 @@ log
log "$(basename "$PATCH_DIR")"
+log "Using PATCH_FILE=$PATCH_FILE"
+
if [ -f "$PATCH_FILE" ]; then
log " contains text patch"
fi