Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cosmic-files "Open With" creates defunct process when used with nobara rpm installer. Installer works when run from terminal. #1730

Open
GloriousEggroll opened this issue Mar 22, 2025 · 0 comments

Comments

@GloriousEggroll
Copy link

GloriousEggroll commented Mar 22, 2025

On nobara we have a very simple rpm installer that allows users to double click an rpm or "open with" and choose the rpm installer to try to install the rpm with the installer.

The installer itself is a very simple single file python script that just issues rpm and dnf commands:

https://github.com/Nobara-Project/nobara-core-packages/blob/main/nobara-updater/src/rpm_installer.py

It does also issue the xhost commands as a workaround for #1418

If I run it in terminal to install an rpm like this:
nobara-rpm-installer floorp-11.24.0-1.fc41.x86_64.rpm

It runs perfectly fine and does what it's supposed to. It just has a few user gui popups that they have to confirm.

However, in cosmic-files if I open my Downloads folder, right click the rpm 'floorp-11.24.0-1.fc41.x86_64.rpm' and click 'Open with...', then select the nobara rpm installer, it does nothing and creates a defunct cosmic-files process:

tcrider   736397  0.0  0.0      0     0 ?        Z    05:32   0:00 [cosmic-files] <defunct>
tcrider   736423  0.0  0.0      0     0 ?        Z    05:33   0:00 [cosmic-files] <defunct>
tcrider   736838  0.0  0.0      0     0 ?        Z    05:36   0:00 [cosmic-files] <defunct>

Works fine with gnome and kde "open with" options.

Steps to reproduce

You'll need your distro equivalent of these python imports:

import tkinter as tk
from tkinter import messagebox
from tkinter import ttk
import subprocess
import os
import sys
import rpm
import logging
import datetime
import time
import threading
import queue

Modified test version of the rpm installer -- won't run any commands, should get a simple popup asking if you want to install "test" version 124:

#!/usr/bin/env python3

import tkinter as tk
from tkinter import messagebox
from tkinter import ttk
import subprocess
import os
import sys
import rpm
import logging
import datetime
import time
import threading
import queue

def validate_rpm_file(rpm_file):
    if not rpm_file:
        return False

    # Convert to lowercase for case-insensitive comparison
    rpm_file = rpm_file.lower()

    # Check if it ends with .rpm but not .src.rpm
    if rpm_file.endswith('.rpm') and not rpm_file.endswith('.src.rpm'):
        return True
    else:
        return False

# Set up logging
def setup_logging():
    log_dir = "~/.local/share/nobara-updater/rpm-installer/"
    log_file = f"log_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.log"

    # Expand tilde to home directory
    log_dir = os.path.expanduser(log_dir)
    log_file = os.path.join(log_dir, log_file)

    # Create log directory if it doesn't exist
    os.makedirs(log_dir, exist_ok=True)

    logging.basicConfig(
        level=logging.ERROR,
        filename=log_file,
        format='%(asctime)s - %(levelname)s - %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S'
    )
    return log_file

# Function to get package name and version from RPM file
def get_rpm_info(rpm_file):
    try:
        # Get RPM name
        rpm_name_command = ["rpm", "-qp", "--qf", "%{NAME}\n", rpm_file]
        rpm_name_process = subprocess.Popen(rpm_name_command, stdout=subprocess.PIPE, text=True)
        rpm_name = rpm_name_process.communicate()[0].strip()

        # Get RPM version
        rpm_version_command = ["rpm", "-qp", "--qf", "%{VERSION}\n", rpm_file]
        rpm_version_process = subprocess.Popen(rpm_version_command, stdout=subprocess.PIPE, text=True)
        rpm_version = rpm_version_process.communicate()[0].strip()

        return rpm_name, rpm_version
    except Exception as e:
        messagebox.showerror("Error", f"Failed to get RPM info: {str(e)}")
        return None, None

def get_installed_package_info(rpm_name):
    try:
        # Check if package is installed
        result = subprocess.run(["rpm", "-q", rpm_name], capture_output=True, text=True)

        if result.returncode != 0:
            return False, None

        # Get currently installed version
        result = subprocess.run(["rpm", "-q", "--qf", "%{VERSION}\n", rpm_name], capture_output=True, text=True)
        current_version = result.stdout.strip()

        return True, current_version

    except Exception as e:
        messagebox.showerror("Error", f"Failed to check installed version: {str(e)}")
        return False, None

# Function to check if package is installed and compare versions
def check_installed_version(rpm_name, version):
    installed, current_version = get_installed_package_info(rpm_name)

    if not installed:
        return False, version
    else:
        return True, current_version

class NoticeDialog:
    def __init__(self, master, message):
        self.dialog = tk.Toplevel(master)
        self.dialog.title("Install/Update in progress...")
        self.dialog.geometry("300x100")
        self.dialog.resizable(False, False)
        print(message)

        self.label = tk.Label(self.dialog, text=message, wraplength=280, justify=tk.CENTER)
        self.label.pack(padx=10, pady=10)

        self.close_function = None

    def set_close_function(self, func):
        self.close_function = func

    def close(self):
        self.dialog.destroy()

def close_dialog():
    # Assuming there's only one NoticeDialog open at a time
    dialog = globals().get('notice')
    if dialog:
        dialog.close()

def show_notice(message):
    global notice
    notice = NoticeDialog(root, message)
    notice.dialog.update_idletasks()  # Force the dialog to appear

def show_waiting_dialog(rpm_name, status, rpm_file):
    if status == "install":
        status_text_complete = "Install"
    else:
        status_text_complete = "Update"

    try:
        result = subprocess.run(["pkexec", "dnf", status, "-y", rpm_file],
                                capture_output=True, text=True)

        if result.returncode != 0:
            raise subprocess.CalledProcessError(result.returncode, result.args, output=result.stdout, stderr=result.stderr)
        close_dialog()
        messagebox.showinfo("Success", f"{status_text_complete} successful for {rpm_name}", parent=root)
        sys.exit(0)
    except subprocess.CalledProcessError as e:
        error_message = f"Failed to {status} {rpm_name}\n\nAn error occurred:\n{e}"
        logging.error(error_message)
        close_dialog()
        messagebox.showerror("Error", error_message + "\n\nLog file location: " + setup_logging(), parent=root)
        sys.exit(0)

def cleanup_xhost():
    """Cleanup function to run xhost on exit"""
    try:
        subprocess.run(["xhost", "-si:localuser:root"])
    except Exception as e:
        logger.error(f"Failed to run xhost cleanup: {e}")

# Main function
def main():
    subprocess.run(["xhost", "si:localuser:root"])
    global root
    root = tk.Tk()
    root.withdraw()

    rpm_file = sys.argv[1]

    #if not validate_rpm_file(rpm_file):
    #    messagebox.showerror("Invalid RPM file", "The specified file must end with '.rpm' but not '.src.rpm'")
    #    sys.exit(1)

    # Get RPM information
    #rpm_info = get_rpm_info(rpm_file)
    #if not rpm_info:
    #    return

    #rpm_name, version = rpm_info

    # Check if package is installed
    #installed, current_version = check_installed_version(rpm_name, version)
    installed = False
    current_version = "123"
    rpm_name = "test"
    version = "124"

    if not installed:
        # Package not installed, ask user if they want to install it
        confirm_install = messagebox.askyesno("Confirm Installation",
                                              f"Do you want to install {rpm_name} version {version}?")
        status = "install"

        if confirm_install:
            status_text = "Installing"
            show_notice(f"{status_text}, please wait")
            root.update_idletasks()
            root.update()
            time.sleep(2)
            show_waiting_dialog(rpm_name, status, rpm_file)
        else:
            messagebox.showinfo("Cancelled", f"Installation of {rpm_name} cancelled.")
            sys.exit(0)

    else:
        # Package installed, check version

        if version > current_version:
            update = messagebox.askyesno("Update Confirmation",
                                        f"{rpm_name} version ({current_version}) is already installed, but the RPM version you've opened is more recent ({version}). Do you want to update {rpm_name} {current_version} -> {version}?")
            status = "update"

            if update:
                status_text = "Updating"
                show_notice(f"{status_text}, please wait")
                root.update_idletasks()
                root.update()
                time.sleep(2)
                show_waiting_dialog(rpm_name, status, rpm_file)
            else:
                messagebox.showinfo("Cancelled", f"Update of {rpm_name} cancelled.")
                sys.exit(0)
        else:
            if version <= current_version:
                messagebox.showinfo("Version Information",
                                f"{rpm_name} is already installed and the version you are trying to install ({version}) is older or the same as the current version ({current_version}).")
                sys.exit(0)

    root.mainloop()

if __name__ == "__main__":
    try:
        # Your main application code here
        main()
    finally:
        # This ensures cleanup runs even if main() throws an exception
        cleanup_xhost()

sudo chmod +x nobara-rpm-installer
cp nobara-rpm-installer /usr/bin/

place in /usr/share/applications/nobara-rpm-installer.desktop to allow it to show up in "Open with...":

[Desktop Entry]
Name=RPM Installer
MimeType=application/x-rpm
Exec=/usr/bin/nobara-rpm-installer
Icon=com.github.nobaraproject.nobarawelcome
Type=Application
Categories=Utility;

Run in terminal to verify it works (should get popup asking to install test 124:
nobara-rpm-installer bogus123.rpm

You should get a simple popup asking if you want to install "test" version 124

Then try with 'Open with..." on any valid *.rpm file.

I've noticed cosmic-files also doesnt bother with file extensions, as simply naming a bogus file ending with .rpm did not work to make it recognized as an rpm.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant