From aea5f4ccc0544baf6dde46d10f8704a2ecd936ce Mon Sep 17 00:00:00 2001 From: coderus Date: Wed, 6 Feb 2019 09:51:38 +0300 Subject: [PATCH] [init] pm4 --- daemon/daemon.pro | 66 +++++ daemon/dbus/org.coderus.patchmanager.conf | 16 ++ daemon/dbus/org.coderus.patchmanager.service | 5 + daemon/dbus/org.coderus.patchmanager.xml | 158 +++++++++++ daemon/environment/01-dbus.conf | 1 + daemon/src/main.cpp | 89 +++++++ daemon/src/patchmanager.cpp | 53 ++++ daemon/src/patchmanager.h | 27 ++ daemon/src/patchmanager_include.h | 9 + .../checkForUpdates-patchmanager.service | 9 + .../checkForUpdates-patchmanager.timer | 13 + daemon/systemd/patchmanager.service | 19 ++ icons/icons.pro | 4 + icons/svgs/icon-m-patchmanager.svg | 215 +++++++++++++++ patchmanager.pro | 50 ++++ preload/preload.pro | 16 ++ preload/src/preloadpatchmanager.c | 247 ++++++++++++++++++ rpm/patchmanager.spec | 190 ++++++++++++++ scripts/pm-apply | 151 +++++++++++ scripts/pm-constants | 22 ++ scripts/pm-unapply | 122 +++++++++ scripts/scripts.pro | 11 + settings/entries/patchmanager.json | 16 ++ settings/pages/patchmanager.qml | 3 + settings/settings.pro | 9 + 25 files changed, 1521 insertions(+) create mode 100644 daemon/daemon.pro create mode 100644 daemon/dbus/org.coderus.patchmanager.conf create mode 100644 daemon/dbus/org.coderus.patchmanager.service create mode 100644 daemon/dbus/org.coderus.patchmanager.xml create mode 100644 daemon/environment/01-dbus.conf create mode 100644 daemon/src/main.cpp create mode 100644 daemon/src/patchmanager.cpp create mode 100644 daemon/src/patchmanager.h create mode 100644 daemon/src/patchmanager_include.h create mode 100644 daemon/systemd/checkForUpdates-patchmanager.service create mode 100644 daemon/systemd/checkForUpdates-patchmanager.timer create mode 100644 daemon/systemd/patchmanager.service create mode 100644 icons/icons.pro create mode 100644 icons/svgs/icon-m-patchmanager.svg create mode 100644 patchmanager.pro create mode 100644 preload/preload.pro create mode 100644 preload/src/preloadpatchmanager.c create mode 100755 rpm/patchmanager.spec create mode 100755 scripts/pm-apply create mode 100644 scripts/pm-constants create mode 100755 scripts/pm-unapply create mode 100644 scripts/scripts.pro create mode 100644 settings/entries/patchmanager.json create mode 100644 settings/pages/patchmanager.qml create mode 100644 settings/settings.pro diff --git a/daemon/daemon.pro b/daemon/daemon.pro new file mode 100644 index 00000000..dd7749b1 --- /dev/null +++ b/daemon/daemon.pro @@ -0,0 +1,66 @@ +TEMPLATE = app +TARGET = patchmanager-daemon + +QT = core dbus network +CONFIG += link_pkgconfig +PKGCONFIG += nemonotifications-qt5 +PKGCONFIG += libsystemd-journal +PKGCONFIG += rpm + +isEmpty(PROJECT_PACKAGE_VERSION) { + BUILD_VERSION = "4.99.99" +} else { + BUILD_VERSION = $$PROJECT_PACKAGE_VERSION +} +message("Version: $$BUILD_VERSION") + +DEFINES += BUILD_VERSION=\\\"$$BUILD_VERSION\\\" + +#HEADERS += \ +# src/patchmanager_include.h + +SOURCES += \ + src/main.cpp \ + src/patchmanager.cpp + +target.path = /usr/sbin +INSTALLS += target + +# DBus service +dbusService.files = dbus/org.coderus.patchmanager.service +dbusService.path = /usr/share/dbus-1/system-services/ +INSTALLS += dbusService + +# DBus interface +dbusInterface.files = dbus/org.coderus.patchmanager.xml +dbusInterface.path = /usr/share/dbus-1/interfaces/ +INSTALLS += dbusInterface + +# DBus config +dbusConf.files = dbus/org.coderus.patchmanager.conf +dbusConf.path = /etc/dbus-1/system.d/ +INSTALLS += dbusConf + +# Systemd +systemd.files = \ + systemd/patchmanager.service \ + systemd/checkForUpdates-patchmanager.service \ + systemd/checkForUpdates-patchmanager.timer +systemd.path = /lib/systemd/system/ +INSTALLS += systemd + +# user environment +env.files = environment/01-dbus.conf +env.path = /var/lib/environment/patchmanager/ +INSTALLS += env + +# dbus interface +pm_dbus_adaptor.files = dbus/org.coderus.patchmanager.xml +pm_dbus_adaptor.source_flags = -c PatchManagerAdaptor +pm_dbus_adaptor.header_flags = -c PatchManagerAdaptor# -i patchmanager_include.h +DBUS_ADAPTORS += pm_dbus_adaptor + +INCLUDEPATH += /usr/include/ + +HEADERS += \ + src/patchmanager.h diff --git a/daemon/dbus/org.coderus.patchmanager.conf b/daemon/dbus/org.coderus.patchmanager.conf new file mode 100644 index 00000000..00650e35 --- /dev/null +++ b/daemon/dbus/org.coderus.patchmanager.conf @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/daemon/dbus/org.coderus.patchmanager.service b/daemon/dbus/org.coderus.patchmanager.service new file mode 100644 index 00000000..c4f12da8 --- /dev/null +++ b/daemon/dbus/org.coderus.patchmanager.service @@ -0,0 +1,5 @@ +[D-BUS Service] +Name=org.coderus.patchmanager +Exec=/bin/false +User=root +SystemdService=patchmanager.service diff --git a/daemon/dbus/org.coderus.patchmanager.xml b/daemon/dbus/org.coderus.patchmanager.xml new file mode 100644 index 00000000..503e38e3 --- /dev/null +++ b/daemon/dbus/org.coderus.patchmanager.xml @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/daemon/environment/01-dbus.conf b/daemon/environment/01-dbus.conf new file mode 100644 index 00000000..f1f16de4 --- /dev/null +++ b/daemon/environment/01-dbus.conf @@ -0,0 +1 @@ +DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/100000/dbus/user_bus_socket diff --git a/daemon/src/main.cpp b/daemon/src/main.cpp new file mode 100644 index 00000000..43f43e64 --- /dev/null +++ b/daemon/src/main.cpp @@ -0,0 +1,89 @@ +#include + +#include +#include +#include +#include + +#include + +static const QString s_newConfigLocation = QStringLiteral("/etc/patchmanager2.conf"); +static const QString s_oldConfigLocation = QStringLiteral("/home/nemo/.config/patchmanager2.conf"); + +QString getLang() +{ + QString lang = QStringLiteral("en_US.utf8"); + + QFile localeConfig(QStringLiteral("/var/lib/environment/nemo/locale.conf")); + + if (!localeConfig.exists() || !localeConfig.open(QFile::ReadOnly)) { + return lang; + } + + while (!localeConfig.atEnd()) { + QString line = localeConfig.readLine().trimmed(); + if (line.startsWith(QStringLiteral("LANG="))) { + lang = line.mid(5); + break; + } + } + + return lang; +} + +int main(int argc, char **argv) +{ + if (getuid() != 0) { + qWarning() << "Not running as root, exiting"; + return 2; + } + + qputenv("NO_PM_PRELOAD", "1"); + + QCoreApplication app(argc, argv); +#ifdef BUILD_VERSION + const QString version = QStringLiteral(BUILD_VERSION); + app.setApplicationVersion(version); +#else + qDebug() << Q_FUNC_INFO << "Patchmanager version unknown!"; + app.setApplicationVersion(QStringLiteral("3.9.9")); +#endif + + qDebug() << Q_FUNC_INFO << "Patchmanager:" << qApp->applicationVersion(); + + const QString lang = getLang(); + qDebug() << Q_FUNC_INFO << "Language:" << lang; + + QTranslator translator; + qDebug() << Q_FUNC_INFO << "Translator loaded:" << + translator.load(QLocale(lang), + QStringLiteral("settings-patchmanager"), + QStringLiteral("-"), + QStringLiteral("/usr/share/translations/"), + QStringLiteral(".qm")); + + qDebug() << Q_FUNC_INFO << "Translator installed:" << + app.installTranslator(&translator); + + QFile preload(QStringLiteral("/etc/ld.so.preload")); + if (preload.exists()) { + qDebug() << Q_FUNC_INFO << "ld.so.preload:"; + if (!preload.open(QFile::ReadOnly)) { + qWarning() << "Can't open ld.so.preload!"; + } + qDebug().noquote() << preload.readAll(); + } else { + qWarning() << Q_FUNC_INFO << "ld.so.preload does not exists!"; + } + + if (!QFileInfo::exists(s_newConfigLocation) && QFileInfo::exists(s_oldConfigLocation)) { + QFile::copy(s_oldConfigLocation, s_newConfigLocation); + } + + if (Q_UNLIKELY(qEnvironmentVariableIsEmpty("DBUS_SESSION_BUS_ADDRESS"))) { + qDebug() << Q_FUNC_INFO << "Injecting DBUS_SESSION_BUS_ADDRESS..." << + qputenv("DBUS_SESSION_BUS_ADDRESS", QByteArrayLiteral("unix:path=/run/user/100000/dbus/user_bus_socket")); + } + + return app.exec(); +} diff --git a/daemon/src/patchmanager.cpp b/daemon/src/patchmanager.cpp new file mode 100644 index 00000000..619ffa59 --- /dev/null +++ b/daemon/src/patchmanager.cpp @@ -0,0 +1,53 @@ +#include + +#include "patchmanager.h" + +static PatchManager* s_pm = nullptr; + +static const QString s_patchFolder = QStringLiteral("/usr/share/patchmanager/patches"); +static const QString s_patchBackup = QStringLiteral("/tmp/patchmanager/patches"); + +static const QString s_scriptApply = QStringLiteral("/usr/libexec/pm-apply"); +static const QString s_scriptUnapply = QStringLiteral("/usr/libexec/pm-unapply"); + +static const QString s_configLocation = QStringLiteral("/etc/patchmanager.conf"); + +static const QString s_sessionBusConnection = QStringLiteral("patchmanagerconnection"); + + +#define NAME(x) #x + +PatchManager *PatchManager::GetInstance() +{ + if (!s_pm) { + s_pm = new PatchManager(); + } + return s_pm; +} + +QString PatchManager::patchFolder() +{ + return s_patchFolder; +} + +PatchManager::PatchManager(QObject *parent) + : QObject(parent) +{ + +} + +bool PatchManager::isReady() const +{ + return m_ready; +} + +void PatchManager::refreshPatchList() +{ + qDebug() << Q_FUNC_INFO; + QMetaObject::invokeMethod(this, NAME(doRefreshPatchList), Qt::QueuedConnection); +} + +void PatchManager::doRefreshPatchList() +{ + +} diff --git a/daemon/src/patchmanager.h b/daemon/src/patchmanager.h new file mode 100644 index 00000000..f8917688 --- /dev/null +++ b/daemon/src/patchmanager.h @@ -0,0 +1,27 @@ +#ifndef PATCHMANAGER_H +#define PATCHMANAGER_H + +#include + +class PatchManager : public QObject +{ + Q_OBJECT +public: + static PatchManager* GetInstance(); + static QString patchFolder(); + + explicit PatchManager(QObject *parent = nullptr); + + bool isReady() const; + + void refreshPatchList(); + +private slots: + void doRefreshPatchList(); + +private: + bool m_ready = false; + +}; + +#endif // PATCHMANAGER_H diff --git a/daemon/src/patchmanager_include.h b/daemon/src/patchmanager_include.h new file mode 100644 index 00000000..701f6d7f --- /dev/null +++ b/daemon/src/patchmanager_include.h @@ -0,0 +1,9 @@ +#ifndef PATCHMANAGER_INCLUDE_H +#define PATCHMANAGER_INCLUDE_H + +#include + +static const QString DBUS_SERVICE_NAME = QStringLiteral("org.coderus.patchmanager"); +static const QString DBUS_PATH_NAME = QStringLiteral("/org/coderus/patchmanager"); + +#endif // PATCHMANAGER_INCLUDE_H diff --git a/daemon/systemd/checkForUpdates-patchmanager.service b/daemon/systemd/checkForUpdates-patchmanager.service new file mode 100644 index 00000000..54071fd7 --- /dev/null +++ b/daemon/systemd/checkForUpdates-patchmanager.service @@ -0,0 +1,9 @@ +[Unit] +Description=Patchmanager check for updates service +Requires=dbus.service +After=dbus.service + +[Service] +Type=oneshot +User=root +ExecStart=/bin/dbus-send --system --print-reply --dest=org.coderus.patchmanager /org/coderus/patchmanager org.coderus.patchmanager.checkForUpdates diff --git a/daemon/systemd/checkForUpdates-patchmanager.timer b/daemon/systemd/checkForUpdates-patchmanager.timer new file mode 100644 index 00000000..76e2b9c6 --- /dev/null +++ b/daemon/systemd/checkForUpdates-patchmanager.timer @@ -0,0 +1,13 @@ +[Unit] +Description=Patchmanager check for updates timer +Requires=dbus.service +After=dbus.service + +[Timer] +Unit=checkForUpdates-patchmanager.service +OnBootSec=15m +OnUnitActiveSec=2h +WakeSystem=true + +[Install] +WantedBy=timers.target diff --git a/daemon/systemd/patchmanager.service b/daemon/systemd/patchmanager.service new file mode 100644 index 00000000..a74609f5 --- /dev/null +++ b/daemon/systemd/patchmanager.service @@ -0,0 +1,19 @@ +[Unit] +Description=Patchmanager service +Requires=dbus.service +After=dbus.service +Before=systemd-user-sessions.service + +[Service] +Type=dbus +BusName=org.coderus.patchmanager +SupplementaryGroups=inet +User=root +EnvironmentFile=-/var/lib/environment/patchmanager/*.conf +ExecStart=/usr/sbin/patchmanager-daemon +TimeoutStartSec=600 +Restart=always +RestartSec=5 + +[Install] +WantedBy=multi-user.target diff --git a/icons/icons.pro b/icons/icons.pro new file mode 100644 index 00000000..18351856 --- /dev/null +++ b/icons/icons.pro @@ -0,0 +1,4 @@ +TEMPLATE = aux +# Configures svg to png +THEMENAME=sailfish-default +CONFIG += sailfish-svg2png \ No newline at end of file diff --git a/icons/svgs/icon-m-patchmanager.svg b/icons/svgs/icon-m-patchmanager.svg new file mode 100644 index 00000000..f123437a --- /dev/null +++ b/icons/svgs/icon-m-patchmanager.svg @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/patchmanager.pro b/patchmanager.pro new file mode 100644 index 00000000..f830d67b --- /dev/null +++ b/patchmanager.pro @@ -0,0 +1,50 @@ +TEMPLATE = subdirs +SUBDIRS = \ + preload \ + daemon \ + scripts \ +# cli \ +# dialog \ +# plugin \ + settings \ + icons + +OTHER_FILES += rpm/patchmanager.spec + +TRANSLATIONS += translations/settings-patchmanager-*.ts + +TS_FILE = $${_PRO_FILE_PWD_}/translations/settings-patchmanager.ts +HAVE_TRANSLATIONS = 0 + +# Translation source directories +TRANSLATION_SOURCE_CANDIDATES = $${_PRO_FILE_PWD_} +for(dir, TRANSLATION_SOURCE_CANDIDATES) { + exists($$dir) { + TRANSLATION_SOURCES += $$dir + } +} + +# prefix all TRANSLATIONS with the src dir +# the qm files are generated from the ts files copied to out dir +for(t, TRANSLATIONS) { + TRANSLATIONS_IN += $${_PRO_FILE_PWD_}/$$t + TRANSLATIONS_OUT += $${OUT_PWD}/$$t + HAVE_TRANSLATIONS = 1 +} + +qm.files = $$replace(TRANSLATIONS_OUT, \.ts, .qm) +qm.path = /usr/share/translations +qm.CONFIG += no_check_exist + +# update the ts files in the src dir and then copy them to the out dir +qm.commands += lupdate -noobsolete $${TRANSLATION_SOURCES} -ts $${TS_FILE} && \ + mkdir -p translations && \ + [ \"$${OUT_PWD}\" != \"$${_PRO_FILE_PWD_}\" -a $$HAVE_TRANSLATIONS -eq 1 ] && \ + cp -af $${TRANSLATIONS_IN} $${OUT_PWD}/translations || : + +# create the qm files +qm.commands += ; [ $$HAVE_TRANSLATIONS -eq 1 ] && lrelease -nounfinished $${TRANSLATIONS_OUT} || : + +INSTALLS += qm + +OTHER_FILES += $$TRANSLATIONS diff --git a/preload/preload.pro b/preload/preload.pro new file mode 100644 index 00000000..908c393d --- /dev/null +++ b/preload/preload.pro @@ -0,0 +1,16 @@ +TEMPLATE = lib +QT = +CONFIG += plugin +QMAKE_CFLAGS += -std=c11 + +LIBS = -ldl + +SOURCES += \ + src/preloadpatchmanager.c + +TARGET = preloadpatchmanager +target.path = /usr/lib + +INSTALLS = target + +INCLUDEPATH += /usr/include diff --git a/preload/src/preloadpatchmanager.c b/preload/src/preloadpatchmanager.c new file mode 100644 index 00000000..8a1996a3 --- /dev/null +++ b/preload/src/preloadpatchmanager.c @@ -0,0 +1,247 @@ +#define _GNU_SOURCE + +//#define NO_INTERCEPT +#define ALLOW_ALL_USERS + +#include +#include +#include + +typedef int (*orig_open_f_type)(const char *pathname, int flags, ...); + +static orig_open_f_type orig_open = NULL; +static orig_open_f_type orig_open64 = NULL; + +#ifndef NO_INTERCEPT + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define SERVER_PATH "/tmp/patchmanager-socket" +#define ENV_NO_PRELOAD "NO_PM_PRELOAD" +#define ENV_DEBUG "PM_PRELOAD_DEBUG" + +static const char *blacklist_paths_startswith[] = { + "/dev", + "/sys", + "/proc", + "/run", + "/tmp", +}; + +static const char *blacklist_paths_equal[] = { + "/", +}; + +static int debug_output() { + static int debug_output_read = 0; + static int debug_output_value = 0; + + if (!debug_output_read) { + debug_output_value = getenv(ENV_DEBUG) ? 1 : 0; + debug_output_read = 1; + } + + return debug_output_value; +} + +static void pm_name(char new_name[]) { + int sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + struct sockaddr_un serveraddr; + memset(&serveraddr, 0, sizeof(serveraddr)); + serveraddr.sun_family = AF_UNIX; + strcpy(serveraddr.sun_path, SERVER_PATH); + int result = connect(sockfd, (struct sockaddr *)&serveraddr, SUN_LEN(&serveraddr)); + + if (result < 0) { + if (debug_output()) { + fprintf(stderr, "[pm_name] error connecting to socket\n"); + } + close(sockfd); + return; + } + + int sn = write(sockfd, new_name, strlen(new_name)); + if (sn <= 0) { + if (debug_output()) { + fprintf(stderr, "[pm_name] error sending to socket\n"); + } + close(sockfd); + return; + } + + char buf_name[PATH_MAX]; + memset(buf_name, 0, sizeof(buf_name)); + int rn = read(sockfd, buf_name, sizeof(buf_name) - 1); + if (rn > 0) { + strcpy(new_name, buf_name); + } else { + if (debug_output()) { + fprintf(stderr, "[pm_name] error reading from socket\n"); + } + } + + close(sockfd); +} + +static uid_t nemo_uid() +{ + static struct passwd *nemo_pwd; + + if (!nemo_pwd) { + nemo_pwd = getpwnam("nemo"); + if (!nemo_pwd) { + return 100000; + } + } + + return nemo_pwd->pw_uid; +} + +static int pm_validate_uid(uid_t uid) +{ +#ifdef ALLOW_ALL_USERS + return 1; +#else // #ifdef ALLOW_ALL_USERS + return uid == nemo_uid(); +#endif // #ifdef ALLOW_ALL_USERS +} + +static int pm_validate_flags(int flags) +{ + return (flags & (O_APPEND | O_WRONLY | O_RDWR | O_TRUNC | O_CREAT | O_NOCTTY | O_TMPFILE | O_SYNC | O_DSYNC | O_DIRECTORY | O_DIRECT)) == 0; +} + +static int pm_validate_name(const char *name) +{ + char dir_name[PATH_MAX]; + strcpy(dir_name, name); + dirname(dir_name); + + for (unsigned int i = 0; i < sizeof(blacklist_paths_equal) / sizeof(*blacklist_paths_equal); i++) { + const char *blacklisted = blacklist_paths_equal[i]; + if (strcmp(blacklisted, dir_name) == 0) { + return 0; + } + } + + for (unsigned int i = 0; i < sizeof(blacklist_paths_startswith) / sizeof(*blacklist_paths_startswith); i++) { + const char *blacklisted = blacklist_paths_startswith[i]; + if (strncmp(blacklisted, name, strlen(blacklisted)) == 0) { + return 0; + } + } + return 1; +} + +static int no_preload() { + static int pm_preload_read = 0; + static int no_pm_preload = 0; + + if (!pm_preload_read) { + no_pm_preload = getenv(ENV_NO_PRELOAD) ? 1 : 0; + pm_preload_read = 1; + } + + return no_pm_preload; +} + +#endif // #ifndef NO_INTERCEPT + +int open64(const char *pathname, int flags, ...) +{ + if (!orig_open64) { + orig_open64 = (orig_open_f_type)dlsym(RTLD_NEXT, "open64"); + } + + va_list args; + va_start(args, flags); + int mode = va_arg(args, int); + va_end(args); + +#ifndef NO_INTERCEPT + + char new_name[PATH_MAX]; + realpath(pathname, new_name); + + const int d_no_preload = no_preload(); + const int d_pm_validate_uid = pm_validate_uid(getuid()); + const int d_pm_validate_flags = pm_validate_flags(flags); + const int d_pm_validate_name = pm_validate_name(new_name); + + if (debug_output()) { + char dir_name[PATH_MAX]; + strcpy(dir_name, new_name); + dirname(dir_name); + + fprintf(stderr, "[open64] pid: %d, path: %s (%s), dir: %s, flags: %d, mode: %d, no_preload: %d, validate_uid: %d, validate_flags: %d, validate_name: %d\n", + getpid(), new_name, pathname, dir_name, flags, mode, d_no_preload, d_pm_validate_uid, d_pm_validate_flags, d_pm_validate_name); + } + + if (!d_no_preload && d_pm_validate_uid && d_pm_validate_flags && d_pm_validate_name) { + pm_name(new_name); + if (debug_output()) { + fprintf(stderr, "[open64] new_name: %s\n", new_name); + } + return orig_open64(new_name, flags, mode); + } + +#endif // #ifndef NO_INTERCEPT + + return orig_open64(pathname, flags, mode); +} + + +int open(const char *pathname, int flags, ...) +{ + if (!orig_open) { + orig_open = (orig_open_f_type)dlsym(RTLD_NEXT, "open"); + } + + va_list args; + va_start(args, flags); + int mode = va_arg(args, int); + va_end(args); + +#ifndef NO_INTERCEPT + + char new_name[PATH_MAX]; + realpath(pathname, new_name); + + const int d_no_preload = no_preload(); + const int d_pm_validate_uid = pm_validate_uid(getuid()); + const int d_pm_validate_flags = pm_validate_flags(flags); + const int d_pm_validate_name = pm_validate_name(new_name); + + if (debug_output()) { + char dir_name[PATH_MAX]; + strcpy(dir_name, new_name); + dirname(dir_name); + + fprintf(stderr, "[open] pid: %d, path: %s (%s), dir: %s, flags: %d, mode: %d, no_preload: %d, validate_uid: %d, validate_flags: %d, validate_name: %d\n", + getpid(), new_name, pathname, dir_name, flags, mode, d_no_preload, d_pm_validate_uid, d_pm_validate_flags, d_pm_validate_name); + } + + if (!d_no_preload && d_pm_validate_uid && d_pm_validate_flags && d_pm_validate_name) { + pm_name(new_name); + if (debug_output()) { + fprintf(stderr, "[open] new_name: %s\n", new_name); + } + return orig_open(new_name, flags, mode); + } + +#endif // #ifndef NO_INTERCEPT + + return orig_open(pathname, flags, mode); +} diff --git a/rpm/patchmanager.spec b/rpm/patchmanager.spec new file mode 100755 index 00000000..120ee447 --- /dev/null +++ b/rpm/patchmanager.spec @@ -0,0 +1,190 @@ +%define theme sailfish-default + +%{!?qtc_qmake5:%define qtc_qmake5 %qmake5} +%{!?qtc_make:%define qtc_make make} + +Name: patchmanager + +Summary: Patchmanager allows you to manage Sailfish OS patches +Version: 4.99.99 +Release: 1 +Group: Qt/Qt +License: TODO +URL: https://github.com/sailfishos-patches/patchmanager +Source0: patchmanager-%{version}.tar.bz2 +Requires: unzip +Requires: patch +Conflicts: jolla-settings-patchmanager +Obsoletes: jolla-settings-patchmanager +Conflicts: patchmanager-ui +Obsoletes: patchmanager-ui +Conflicts: prepatch +Obsoletes: prepatch +BuildRequires: pkgconfig(Qt5Core) +BuildRequires: pkgconfig(Qt5DBus) +BuildRequires: pkgconfig(Qt5Qml) +BuildRequires: pkgconfig(Qt5Quick) +BuildRequires: pkgconfig(mlite5) +BuildRequires: pkgconfig(sailfishapp) >= 0.0.10 +BuildRequires: sailfish-svg2png >= 0.1.5 +BuildRequires: pkgconfig(nemonotifications-qt5) +BuildRequires: qt5-qtdeclarative-devel-tools +BuildRequires: pkgconfig(libsystemd-journal) +BuildRequires: qt5-qttools-linguist +BuildRequires: pkgconfig(rpm) + +%description +patchmanager allows managing Sailfish OS patches +on your device easily. + +%prep +%setup -q -n patchmanager-%{version} + +%build + +%qtc_qmake5 "PROJECT_PACKAGE_VERSION=%{version}" +%qtc_make %{?_smp_mflags} + +%install +rm -rf %{buildroot} + +%qmake5_install + +/usr/lib/qt5/bin/qmlplugindump -v -noinstantiate -nonrelocatable org.coderus.patchmanager 2.0 %{buildroot}%{_libdir}/qt5/qml > %{buildroot}%{_libdir}/qt5/qml/org/coderus/patchmanager/plugin.qmltypes |: +sed -i 's#%{buildroot}##g' %{buildroot}%{_libdir}/qt5/qml/org/coderus/patchmanager/plugin.qmltypes + +mkdir -p %{buildroot}/lib/systemd/system/multi-user.target.wants/ +ln -s ../patchmanager.service %{buildroot}/lib/systemd/system/multi-user.target.wants/ + +mkdir -p %{buildroot}/lib/systemd/system/timers.target.wants/ +ln -s ../checkForUpdates-patchmanager.timer %{buildroot}/lib/systemd/system/timers.target.wants/ + +mkdir -p %{buildroot}/usr/lib/systemd/user/lipstick.service.wants/ +ln -s ../lipstick-patchmanager.service %{buildroot}/usr/lib/systemd/user/lipstick.service.wants/ + +mkdir -p %{buildroot}%{_datadir}/patchmanager/patches + +%pre +export NO_PM_PRELOAD=1 +case "$*" in +1) +echo Installing package +;; +2) +echo Upgrading package +// unapply patches if pm2 is installed +if [ "$(rpm -q --qf "%{VERSION}" patchmanager | head -c 1)" == "2" ] +then + if [ ! -d /var/lib/patchmanager/ausmt/patches/ ] + then + exit 0 + fi + if [ "$(ls -A /var/lib/patchmanager/ausmt/patches/)" ] + then + echo "Unapply all patches before upgrade!" + exit 1 + fi +fi +;; +*) echo case "$*" not handled in pre +esac + +%preun +export NO_PM_PRELOAD=1 +case "$*" in +0) +echo Uninstalling package +#if [ -d /var/lib/patchmanager/ausmt/patches/sailfishos-patchmanager-unapplyall ]; then +#/usr/sbin/patchmanager -u sailfishos-patchmanager-unapplyall || true +#fi + +systemctl stop patchmanager.service +;; +1) +echo Upgrading package +;; +*) echo case "$*" not handled in preun +esac + +%post +export NO_PM_PRELOAD=1 +case "$*" in +1) +echo Installing package +#/usr/sbin/patchmanager -a sailfishos-patchmanager-unapplyall || true +;; +2) +echo Upgrading package +#/usr/sbin/patchmanager -a sailfishos-patchmanager-unapplyall || true +;; +*) echo case "$*" not handled in post +esac +if grep libpreloadpatchmanager /etc/ld.so.preload > /dev/null; then + echo "Preload already exists" +else + echo /usr/lib/libpreloadpatchmanager.so >> /etc/ld.so.preload +fi +/sbin/ldconfig +dbus-send --system --type=method_call \ +--dest=org.freedesktop.DBus / org.freedesktop.DBus.ReloadConfig +systemctl daemon-reload +systemctl-user daemon-reload +systemctl restart patchmanager.service +systemctl restart patchmanager.timer + +%postun +export NO_PM_PRELOAD=1 +case "$*" in +0) +echo Uninstalling package +sed -i "/libpreloadpatchmanager/ d" /etc/ld.so.preload +rm -rf /tmp/patchmanager |: +rm -f /tmp/patchmanager-socket |: +;; +1) +echo Upgrading package +;; +*) echo case "$*" not handled in postun +esac +/sbin/ldconfig +dbus-send --system --type=method_call \ +--dest=org.freedesktop.DBus / org.freedesktop.DBus.ReloadConfig +systemctl daemon-reload +systemctl-user daemon-reload + +%files +%defattr(-,root,root,-) +%{_bindir}/patchmanager-dialog +%{_bindir}/patchmanager +%{_sbindir}/patchmanager-daemon +%dir %{_datadir}/patchmanager/patches +%{_datadir}/patchmanager/tools +%{_datadir}/dbus-1/ +%{_sysconfdir}/dbus-1/system.d/ +/lib/systemd/system/patchmanager.service +/lib/systemd/system/multi-user.target.wants/patchmanager.service +/lib/systemd/system/checkForUpdates-patchmanager.service +/lib/systemd/system/checkForUpdates-patchmanager.timer +/lib/systemd/system/timers.target.wants/checkForUpdates-patchmanager.timer +%{_sharedstatedir}/environment/patchmanager/10-dbus.conf +%{_libdir}/systemd/user/patchmanager.service +%{_libdir}/systemd/user/lipstick-patchmanager.service +%{_libdir}/systemd/user/lipstick.service.wants/lipstick-patchmanager.service +%{_libdir}/libpreloadpatchmanager.so + +%attr(0755,root,root-) %{_libexecdir}/pm_apply +%attr(0755,root,root-) %{_libexecdir}/pm_unapply + +%{_libdir}/qt5/qml/org/coderus/patchmanager +%{_datadir}/patchmanager/data +%{_datadir}/translations +%{_datadir}/jolla-settings/pages/patchmanager +%{_datadir}/jolla-settings/entries/patchmanager.json +%{_datadir}/patchmanager/icons/icon-m-patchmanager.png + +%{_datadir}/themes/%{theme}/meegotouch/z1.0/icons/*.png +%{_datadir}/themes/%{theme}/meegotouch/z1.25/icons/*.png +%{_datadir}/themes/%{theme}/meegotouch/z1.5/icons/*.png +%{_datadir}/themes/%{theme}/meegotouch/z1.5-large/icons/*.png +%{_datadir}/themes/%{theme}/meegotouch/z1.75/icons/*.png +%{_datadir}/themes/%{theme}/meegotouch/z2.0/icons/*.png diff --git a/scripts/pm-apply b/scripts/pm-apply new file mode 100755 index 00000000..c552f8d3 --- /dev/null +++ b/scripts/pm-apply @@ -0,0 +1,151 @@ +#!/bin/bash + +# +# Some constants +# +. /etc/pm-constants + +# +# Here starts the part where all the magic happens +# + +# +# Helper functions that do all the needed heavy work +# + +log() { + echo "$@" | tee -a "$PM_LOG_FILE" +} + +failure() { + log + log "*** FAILED ***" + log + + exit 1 +} + +success() { + log + log "*** SUCCESS ***" + log + + exit 0 +} + +test_if_applied() { + if [ -f "$PATCH_PATH" ]; then + log + log "----------------------------------" + log "Test if already applied patch" + log "----------------------------------" + log + + $PATCH_EXEC -R -p 1 -d "$ROOT_DIR" --dry-run < "$PATCH_PATH" 2>&1 | tee -a "$PM_LOG_FILE" ; test ${PIPESTATUS[0]} -eq 0 + + if [ $? -eq 0 ]; then + success + fi + fi +} + +verify_text_patch() { + if [ -f "$PATCH_PATH" ]; then + log + log "----------------------------------" + log "Dry running patch" + log "----------------------------------" + log + + $PATCH_EXEC -p 1 -d "$ROOT_DIR" --dry-run < "$PATCH_PATH" 2>&1 | tee -a "$PM_LOG_FILE" ; test ${PIPESTATUS[0]} -eq 0 + + if [ $? -ne 0 ]; then + failure + fi + fi +} + +install_text_patch() { + if [ -f "$PATCH_PATH" ]; then + log + log "----------------------------------" + log "Applying text patch" + log "----------------------------------" + log + + $PATCH_EXEC -p 1 -d "$ROOT_DIR" --no-backup-if-mismatch < "$PATCH_PATH" 2>&1 | tee -a "$PM_LOG_FILE" ; test ${PIPESTATUS[0]} -eq 0 + fi +} + +create_backup_patch() { + log + log "----------------------------------" + log "Creating backup patch" + log "----------------------------------" + log + + mkdir -p "$PM_PATCH_BACKUP_DIR" > /dev/null 2>&1 + cp -f "$PATCH_DIR/$PATCH_NAME" "$PM_PATCH_BACKUP_DIR" > /dev/null 2>&1 + log "Created backup" + log " backup: $PM_PATCH_BACKUP_DIR" +} + +# +# Bunch of safety checks and required initializations +# + +mkdir -p "$PM_VAR_DIR" > /dev/null 2>&1 + +if [ ! -f "$PM_LOG_FILE" ]; then + log "*** Patch Log Created by Apply $(date) ***" +fi + +log +log "----------------------------------" +log "PM APPLY $(date)" +log "----------------------------------" +log + +log $(basename "$PATCH_DIR") + +if [ -f "$PATCH_PATH" ]; then + log " contains text patch" +fi + +log + +if [ ! -x "$PATCH_EXEC" ]; then + log "ERROR: Cannot find $PATCH_EXEC" + failure +fi + +if [ -z "$PATCH_DIR" ]; then + log "ERROR: PATCH_DIR must be given for installing" + failure +fi + +if [ ! -d "$PATCH_DIR" ]; then + log "ERROR: $PATCH_DIR is not a directory or does not exist!" + failure +fi + +if [ ! -f "$PATCH_PATH" ]; then + log "ERROR: Cannot find patch file !" + failure +fi + +# +# The main function that controls all the magic stuff +# + +test_if_applied + +verify_text_patch + +install_text_patch + +create_backup_patch + +success + +exit 0 diff --git a/scripts/pm-constants b/scripts/pm-constants new file mode 100644 index 00000000..73d723cf --- /dev/null +++ b/scripts/pm-constants @@ -0,0 +1,22 @@ +# Patchmanager script constants + +# Root +PM_VAR_DIR="/tmp/patchmanager" +PATCH_ROOT_DIR="/usr/share/patchmanager/patches" + +# Paths / Files +PATCH_DIR="$PATCH_ROOT_DIR/$1" + +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" + +# Constants +PATCH_NAME="unified_diff.patch" +PATCH_PATH="$PATCH_DIR/$PATCH_NAME" +PATCH_BACKUP="$PM_PATCH_BACKUP_DIR/$PATCH_NAME" + +ROOT_DIR="/tmp/patchmanager-root" + +# Applications +PATCH_EXEC="/usr/bin/patch" diff --git a/scripts/pm-unapply b/scripts/pm-unapply new file mode 100755 index 00000000..4b3a608c --- /dev/null +++ b/scripts/pm-unapply @@ -0,0 +1,122 @@ +#!/bin/bash + +# +# Some constants +# +. /etc/pm-constants + +if [ -f "$PATCH_BACKUP" ]; then + PATCH_FILE="$PATCH_BACKUP" +else + PATCH_FILE="$PATCH_PATH" +fi + +# +# Here starts the part where all the magic happens +# + +# +# Helper functions that do all the needed heavy work +# + +log() { + echo "$@" 2>&1 | tee -a "$PM_LOG_FILE" +} + +failure() { + log + log "*** FAILED ***" + log + + exit 1 +} + +success() { + log + log "*** SUCCESS ***" + log + + exit 0 +} + +verify_text_patch() { + if [ -f "$PATCH_FILE" ]; then + log + log "----------------------------------" + log "Dry running patch" + log "----------------------------------" + log + + $PATCH_EXEC -R -p 1 -d "$ROOT_DIR" --dry-run < "$PATCH_FILE" 2>&1 | tee -a "$PM_LOG_FILE" ; test ${PIPESTATUS[0]} -eq 0 + fi +} + +remove_text_patch() { + if [ -f "$PATCH_FILE" ]; then + log + log "----------------------------------" + log "Unapplying text patch" + log "----------------------------------" + log + + $PATCH_EXEC -R -p 1 -d "$ROOT_DIR" --no-backup-if-mismatch < "$PATCH_FILE" 2>&1 | tee -a "$PM_LOG_FILE" ; test ${PIPESTATUS[0]} -eq 0 + fi +} + +clean_backup_patch() { + log + log "----------------------------------" + log "Cleaning backup patch" + log "----------------------------------" + log + + rm -rf "$PM_PATCH_BACKUP_DIR" > /dev/null 2>&1 + log "Removed backup" + log " backup: $PM_PATCH_BACKUP_DIR" +} + +# +# Bunch of safety checks and required initializations +# + +if [ ! -f "$PM_LOG_FILE" ]; then + log "*** Patch Log Created by Unapply $(date) ***" +fi + +log +log "----------------------------------" +log "PM UNAPPLY $(date)" +log "----------------------------------" +log + +log $(basename "$PATCH_DIR") + +if [ -f "$PATCH_FILE" ]; then + log " contains text patch" +fi + +log + +if [ ! -x "$PATCH_EXEC" ]; then + log "ERROR: Cannot find $PATCH_EXEC" + failure +fi + +if [ ! -f "$PATCH_FILE" ]; then + log "ERROR: Cannot find patch file !" + failure +fi + +# +# The main function that controls all the magic stuff +# + +verify_text_patch + +remove_text_patch + +clean_backup_patch + +success + +exit 0 diff --git a/scripts/scripts.pro b/scripts/scripts.pro new file mode 100644 index 00000000..ecc3cc4d --- /dev/null +++ b/scripts/scripts.pro @@ -0,0 +1,11 @@ +TEMPLATE = aux + +scripts.files = \ + pm-apply \ + pm-unapply +scripts.path = /usr/libexec + +constants.files = pm-constants +constants.path = /etc + +INSTALLS += scripts constants diff --git a/settings/entries/patchmanager.json b/settings/entries/patchmanager.json new file mode 100644 index 00000000..603cd579 --- /dev/null +++ b/settings/entries/patchmanager.json @@ -0,0 +1,16 @@ +{ + "translation_catalog": "settings-patchmanager", + "entries": [ + { + "path": "system_settings/look_and_feel/patchmanager", + "type": "page", + "title": "Patchmanager", + "translation_id": "Patchmanager", + "icon": "image://theme/icon-m-patchmanager", + "order": 2000, + "params": { + "source": "/usr/share/jolla-settings/pages/patchmanager/patchmanager.qml" + } + } + ] +} diff --git a/settings/pages/patchmanager.qml b/settings/pages/patchmanager.qml new file mode 100644 index 00000000..324d5b79 --- /dev/null +++ b/settings/pages/patchmanager.qml @@ -0,0 +1,3 @@ +import org.coderus.patchmanager 4.0 + +PatchManagerPage {} diff --git a/settings/settings.pro b/settings/settings.pro new file mode 100644 index 00000000..43cf457a --- /dev/null +++ b/settings/settings.pro @@ -0,0 +1,9 @@ +TEMPLATE = aux + +pages.path = /usr/share/jolla-settings/pages/patchmanager +pages.files = pages/patchmanager.qml + +entries.path = /usr/share/jolla-settings/entries +entries.files = entries/patchmanager.json + +INSTALLS += entries pages