ISO icon indicating copy to clipboard operation
ISO copied to clipboard

Automatically open Filer when disk is attached

Open probonopd opened this issue 4 years ago • 17 comments

Automatically open Filer when disk is attached

Pending on: https://github.com/vermaden/automount/issues/21 cc @vermaden

probonopd avatar Jan 01 '21 01:01 probonopd

Filer critically needs its environment variables.

Left: Without environment variables (as invoked through automount). Right: With environment variables.

image

probonopd avatar Jan 01 '21 01:01 probonopd

Prepare a proof-of-concept for the following:

  1. Implement the File Manager D-Bus Interface https://github.com/dirk-olmes/dbus-file-manager/blob/master/expose-filemanager.py#L31-L53 and expose it on the System bus (maybe on the Session bus as well?)
  2. Write a tiny command line tool that can be invoked by automount that would call ShowFolders on the System bus

https://github.com/vermaden/automount/issues/21#issuecomment-754124574

Related:

  • https://github.com/lxqt/pcmanfm-qt/issues/1194

probonopd avatar Jan 04 '21 18:01 probonopd

Here is a proof-of-concept, but I fail to publish 'org.freedesktop.FileManager1' on the Service Bus, I can only publish it on the Session Bus. Why? How can this be solved?

#!/usr/bin/env python3

# Based on
# https://raw.githubusercontent.com/dirk-olmes/dbus-file-manager/master/expose-filemanager.py


# This works when running as a user and when using the Session Bus.
# This does NOT work when running as root and when trying to use the System Bus:
# dbus.exceptions.DBusException: org.freedesktop.DBus.Error.AccessDenied: 
# Connection ":1.28" is not allowed to own the service "org.freedesktop.FileManager1" 
# due to security policies in the configuration file
#
# See '!!!' below


import os
from gi.repository import GLib
import dbus.service
from dbus import Interface, SessionBus, SystemBus
from dbus.mainloop.glib import DBusGMainLoop
from os.path import basename, dirname


class FileManagerDBusServiceObject(dbus.service.Object):
    BUS_NAME = 'org.freedesktop.FileManager1'
    INTERFACE = 'org.freedesktop.FileManager1'

    def __init__(self, connection, object_path):
        bus_name = dbus.service.BusName(self.BUS_NAME, connection)
        dbus.service.Object.__init__(self, bus_name, object_path)

    @dbus.service.method(dbus_interface=INTERFACE, in_signature='ass', out_signature='')
    def ShowFolders(self, folders, startup_id):
        print("ShowFolders", folders)
        return None

    @dbus.service.method(dbus_interface=INTERFACE, in_signature='ass', out_signature='')
    def ShowItems(self, items, startup_id):
        print("ShowItems", items)
        for item in items:
        	# Do whatever is needed to open the location in the file manager;
        	# in helloSystem, we use the 'launch' command to open the 'Filer' file manager
	        item = str(item).replace("file://", "")
	        print("Opening", item, "in the file manager")
	        os.system("launch Filer "+  item)
        return None

    @dbus.service.method(dbus_interface=INTERFACE, in_signature='ass', out_signature='')
    def ShowItemProperties(self, items, startup_id):
        print("ShowItemProperties", items)
        return None


if __name__ == '__main__':
    # make sure there is a run loop
    DBusGMainLoop(set_as_default=True)
    
    which_bus_to_use = SystemBus # !!! With SessionBus it works, with SystemBus it fails

    bus = which_bus_to_use()

    # expose the file manager object
    object_path = '/org/freedesktop/FileManager1'
    filemanager = FileManagerDBusServiceObject(bus, object_path)

    # now run the loop forever
    loop = GLib.MainLoop()
    loop.run()

Run e.g., ./show-file.py /home/Downloads to test the D-Bus service:

#!/usr/bin/env python3

# show-fm - Reveal specified items in a file manager window
# https://github.com/grawity/code/blob/master/bin/show-file

import dbus
import os
import sys

urls = ["file://%s" % os.path.abspath(p) for p in sys.argv[1:]]
if not urls:
    print("Usage: %s PATH..." % sys.argv[0], file=sys.stderr)
    exit(2)

bus = dbus.SessionBus()
fm = dbus.Interface(bus.get_object("org.freedesktop.FileManager1",
                                   "/org/freedesktop/FileManager1"),
                    "org.freedesktop.FileManager1")
fm.ShowItems(urls, "")

Or just use gdbus call, For calling the Session Bus:

gdbus call --session --dest org.freedesktop.FileManager1 --object-path /org/freedesktop/FileManager1 \ 
    --method org.freedesktop.FileManager1.ShowItems \ 
     '["file:///usr"]' "" 

and the System Bus:

gdbus call --system --dest org.freedesktop.FileManager1 --object-path /org/freedesktop/FileManager1 \ 
    --method org.freedesktop.FileManager1.ShowItems \ 
     '["file:///usr"]' "" 

probonopd avatar Jan 04 '21 19:01 probonopd

I suspect that we'd need a .service for FileManager1, but where to get one from?

Looks like here is one: https://github.com/linuxdeepin/dde-qt-dbus-factory/blob/uos/xml/org.freedesktop.FileManager1.xml

Boy is this complicated:

  • Why are there .conf files,/usr/local/etc/dbus-1/system.d/, and what do they do?
  • Why are there .xml files, /usr/local/share/dbus-1/interfaces/, and what do they do? They seem to just contain an XML representation of the very information that gets published on the bus. So why the need for those files when we have a bus? Which package is supposed to own those files given that multiple applications can provide the same D-Bus Service, e.g., 2 file managers being installed at the same time? Also, they do not seem to be related to permissions, so probably not helpful to resolve the issue above.
  • Why are there service files, where do they go, and what do they do? Here is a service file: https://github.com/rodlie/powerkit/blob/master/daemon/powerkitd.conf.in - looks like when people talk about service files they mean .conf files...

probonopd avatar Jan 04 '21 19:01 probonopd

With the friendly help of @rodlie I cobbled together this /usr/local/etc/dbus-1/system.d/org.freedesktop.FileManager1.conf based on https://github.com/rodlie/powerkit/blob/master/daemon/powerkitd.conf.in

<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
  <!-- Only _USER_ user can own the service -->
  <policy user="_USER_">
    <allow own="org.freedesktop.FileManager1"/>
  </policy>
  <!-- Allow anyone in group to invoke methods -->
  <policy group="_GROUP_">
    <allow send_destination="org.freedesktop.FileManager1"/>
    <allow receive_sender="org.freedesktop.FileManager1"/>
  </policy>
  <!-- Allow everything -->
  <policy user="_USER_">
    <allow send_destination="org.freedesktop.FileManager1"/>
    <allow receive_sender="org.freedesktop.FileManager1"/>
  </policy>
</busconfig>

Then ran sudo service dbus restart. When trying to publish on the System Bus, I am still getting

dbus.exceptions.DBusException: org.freedesktop.DBus.Error.AccessDenied: Connection ":1.0" is not allowed to own the service "org.freedesktop.FileManager1" due to security policies in the configuration file

probonopd avatar Jan 04 '21 19:01 probonopd

Replace _USER_ with the owner of the service and set _GROUP_ to the user group that can access the service.

rodlie avatar Jan 04 '21 19:01 rodlie

The owner of the service is the user that is currently logged into Xorg. Is there a way not to have to hardcode the username, as there can be many different users on the same system?

probonopd avatar Jan 04 '21 20:01 probonopd

Then you want a session, not a service. The example above is for a system service.

rodlie avatar Jan 04 '21 20:01 rodlie

  • The file manager is running as the normal logged in user
  • The automounter is running as root
  • I need a way for the automounter to tell the file manager to open a location (basically I need a way for root to tell the normal user's file manager what to do - I thought root has superpowers anyway...)

From what you are saying, do I need a "session on the System Bus" for this then?

probonopd avatar Jan 04 '21 20:01 probonopd

You can't run the service as a user, the service must be system-wide. Just set the owner to root and the group to a common user group (users, disks, whatever). And of course implement the "client" stuff in the file manager.

Or save yourself some time and use udisks (that already does this and more).

rodlie avatar Jan 04 '21 20:01 rodlie

Examples (from my projects)

  • https://github.com/rodlie/qtfm/blob/master/libfm/service.h (a user session "service")
  • https://github.com/rodlie/qtfm/blob/master/libfm/udisks2.h (a udisk client)

rodlie avatar Jan 04 '21 20:01 rodlie

Please make this optional. Various use cases require something other than Filer to be (or remain) in front after a file system is mounted.

grahamperrin avatar Jan 06 '21 08:01 grahamperrin

Can you give an example? (It will be configurable in any case, I am just curious about your use case.)

probonopd avatar Jan 06 '21 18:01 probonopd

For example:

  1. ~~Leafpad~~ FeatherPad
  2. new file
  3. save
  4. observe the Save As dialogue
  5. connect a USB flash drive with a file system that will automatically mount

Expected

  1. an uninterrupted view of the Save As dialogue

grahamperrin avatar Jan 07 '21 07:01 grahamperrin

How can we hook in a "Go To Folder"-like command in placesmodelitem.cpp void PlacesModelMountItem::update()?

In desktopmainwindow.cpp the following is called: Q_EMIT openFolder(gotoFolderDialog->getPath());.

How can we call Q_EMIT openFolder from placesmodelitem.cpp void PlacesModelMountItem::update()?

Do you know @crees?

probonopd avatar Oct 01 '21 09:10 probonopd

foldermodel.cpp has:

g_signal_connect(computerFolder_, "files-added", G_CALLBACK(onFilesAdded), this);

---> Can we use this to open newly added disks automatically?

probonopd avatar Nov 28 '21 14:11 probonopd

https://github.com/helloSystem/LinuxRuntime/issues/2#issuecomment-1647733881 has a working way to allow applications running as root to communicate with the session bus of a normal user. The trick is to use TCP instead of Unix sockets, which seems to be less restrictive about which user accesses it. If anyone knows a simpler/better way, let me know.

But maybe the easier approach would be to have Filer check for new directories appear at /media, and open windows for them.

probonopd avatar Jul 24 '23 12:07 probonopd