Can't connect to org.freedesktop.UDisks via DBus-Python - python

It's the first time I'm using DBus so please bear with me.
This is my code:
import gobject
import pprint
gobject.threads_init()
from dbus import glib
glib.init_threads()
import dbus
bus = dbus.SessionBus()
remote_object = bus.get_object("org.freedesktop.UDisks", # Connection name
"/org/freedesktop/UDisks" # Object's path
)
print ("Introspection data:\n")
print remote_object.Introspect()
print remote_object.get_dbus_method("ListNames",dbus_interface="org.freedesktop.DBus")
for item in remote_object.ListNames():
print item
The error I'm getting is:
dbus.exceptions.DBusException: org.freedesktop.DBus.Error.ServiceUnknown: The name org.freedesktop.UDisks was not provided by any .service files
From the udisk-demon manpage
udisks-daemon provides the org.freedesktop.UDisks service on the system message bus. Users or administrators should never need to start this daemon as it will be automatically started by dbus-daemon(1) whenever an application calls into the org.freedesktop.UDisks service. See the udisks(7) man page for information on how to customize how udisks-daemon works.
EDIT: So it was SystemSession() and not SessionBus()

You can try using DFeet to check if this dbus object really exists.

The following worked for me, but I don't see the ListNames method you used, so I used EnumerateDevices.
import dbus
bus = dbus.SystemBus()
udisks = bus.get_object("org.freedesktop.UDisks", "/org/freedesktop/UDisks")
udisks = dbus.Interface(udisks, 'org.freedesktop.UDisks')
devices = udisks.get_dbus_method('EnumerateDevices')()

Related

Connecting to user dbus as root

If we open a python interpreter normally and enter the following:
import dbus
bus = dbus.SessionBus()
bus.list_names()
We see all the services on the user's session dbus. Now suppose we wanted to do some root-only things in the same script to determine information to pass through dbus, so we run the interpreter with sudo python and run the same thing, we only see a short list of items on the root user's session dbus, and attempting to connect to anything that was on the user dbus with get_object produces a not found error accordingly.
So far I've tried inserting
import os
os.seteuid(int(os.environ['SUDO_UID']))
But this only makes SessionBus() give a org.freedesktop.DBus.Error.NoReply so this is probably nonsense. Is there a way to connect to a user's dbus service as a super user, with the python dbus bindings?
I have little knowledge about DBus, but that question got me curious.
TL;DR: Use dbus.bus.BusConnection with the socket address for the target user and seteuid for gaining access.
First question: What socket does DBus connect to for the session bus?
$ cat list_bus.py
import dbus
print(dbus.SessionBus().list_names())
$ strace -o list_bus.trace python3 list_bus.py
$ grep ^connect list_bus.trace
connect(3, {sa_family=AF_UNIX, sun_path="/run/user/1000/bus"}, 20) = 0
Maybe it relies on environment variables for this? Gotcha!
$ env|grep /run/user/1000/bus
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
Stracing the behaviour from the root account it appears that it does not know the address to connect to. Googling for the variable name got me to the D-Bus Specification, section "Well-known Message Bus Instances".
Second question: Can we connect directly to the socket without having the D-Bus library guess the right address? The dbus-python tutorial states:
For special purposes, you might use a non-default Bus, or a connection which isn’t a Bus at all, using some new API added in dbus-python 0.81.0.
Looking at the changelog, this appears to refer to these:
Bus has a superclass dbus.bus.BusConnection (a connection to a bus daemon, but without the shared-connection semantics or any deprecated API) for the benefit of those wanting to subclass bus daemon connections
Let's try this out:
$ python3
Python 3.9.2 (default, Feb 28 2021, 17:03:44)
>>> from dbus.bus import BusConnection
>>> len(BusConnection("unix:path=/run/user/1000/bus").list_names())
145
How about root access?
# python3
>>> from dbus.bus import BusConnection
>>> len(BusConnection("unix:path=/run/user/1000/bus").list_names())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3/dist-packages/dbus/bus.py", line 124, in __new__
bus = cls._new_for_bus(address_or_type, mainloop=mainloop)
dbus.exceptions.DBusException: org.freedesktop.DBus.Error.NoReply: Did not
receive a reply. Possible causes include: the remote application did not send
a reply, the message bus security policy blocked the reply, the reply timeout
expired, or the network connection was broken.
>>> import os
>>> os.seteuid(1000)
>>> len(BusConnection("unix:path=/run/user/1000/bus").list_names())
143
So this answers the question: Use BusConnection instead of SessionBus and specify the address explicitly, combined with seteuid to gain access.
Bonus: Connect as root without seteuid
Still I'd like to know if it is possible
to access the bus directly as root user, without resorting to seteuid. After
a few search queries, I found a systemd ticket
with this remark:
dbus-daemon is the component enforcing access ... (but you can drop an xml policy file in, to make it so).
This led me to an askubuntu question discussing how to modify the site local session bus policy.
Just to play with it, I ran this in one terminal:
$ cp /usr/share/dbus-1/session.conf session.conf
$ (edit session.conf to modify the include for local customization)
$ diff /usr/share/dbus-1/session.conf session.conf
50c50
< <include ignore_missing="yes">/etc/dbus-1/session-local.conf</include>
---
> <include ignore_missing="yes">session-local.conf</include>
$ cat > session-local.conf
<busconfig>
<policy context="mandatory">
<allow user="root"/>
</policy>
</busconfig>
$ dbus-daemon --config-file session.conf --print-address
unix:abstract=/tmp/dbus-j0r67hLIuh,guid=d100052e45d06f248242109262325b98
$ dbus-daemon --config-file session.conf --print-address
unix:abstract=/tmp/dbus-j0r67hLIuh,guid=d100052e45d06f248242109262325b98
In another terminal, I can not attach to this bus as a root user:
# python3
Python 3.9.2 (default, Feb 28 2021, 17:03:44)
>>> from dbus.bus import BusConnection
>>> address = "unix:abstract=/tmp/dbus-j0r67hLIuh,guid=d100052e45d06f248242109262325b98"
>>> BusConnection(address).list_names()
dbus.Array([dbus.String('org.freedesktop.DBus'), dbus.String(':1.0')], signature=dbus.Signature('s'))
This should also enable accessing all session busses on the system, when installing session-local.conf globally:
# cp session-local.conf /etc/dbus-1/session-local.conf
# kill -HUP 1865 # reload config of my users session dbus-daemon
# python3
>>> from dbus.bus import BusConnection
>>> len(BusConnection("unix:path=/run/user/1000/bus").list_names())
143
And it works - now root can connect to any session bus without resorting to seteuid. Don't forget to
# rm /etc/dbus-1/session-local.conf
if your root user does not need this power.
Bluehorn's answer helped me. I figured I'd share my solution. I'm only a few months into learning Python (coming from just shell scripting) so if this is really wrong and just happens to work on my system please let me know.
These are parts from a daemon I wrote to monitor CPU temps and control the fan speeds in Linux so it needs root permissions. Not sure how well it would work if ran as a regular user when multiple users are logged in. I'm guessing it wouldn't...
import os, pwd
from dbus import SessionBus, Interface
from dbus.bus import BusConnection
# Subclassing dbus.Interface because why not
class Dbus(Interface):
def __init__(self, uid):
method = 'org.freedesktop.Notifications'
path = '/' + method.replace('.', '/')
if os.getuid() == uid:
obj = SessionBus().get_object( method, path )
else:
os.seteuid(uid)
obj = BusConnection( "unix:path=/run/user/" + str(uid) + "/bus" )
obj.get_object( method, path )
super().__init__(obj, method)
# Did this so my notifications would work
# when running as root or non root
class DbusNotificationHandler:
app_icon = r"/path/to/my/apps/icon.png"
name = "MacFanD"
def __init__(self):
loggedIn, users = [ os.getlogin() ], []
for login in pwd.getpwall():
if login.pw_name in loggedIn:
users.append( login.pw_uid )
self.users = []
for i in users:
self.users.append( Dbus(i) )
def notification(self, msg, mtype=None):
if not isinstance(msg, list) or len(msg) < 2:
raise TypeError("Expecting a list of 2 for 'msg' parameter")
hint = {}
if mtype == 'temp':
icon = 'dialog-warning'
hint['urgency'] = 2
db_id = 498237618
timeout = 0
elif mtype == 'warn':
icon = 'dialog-warning'
hint['urgency'] = 2
db_id = 0
timeout = 5000
else:
icon = self.app_icon
hint['urgency'] = 1
db_id = 0
timeout = 5000
for db in self.users:
db.Notify( self.name, db_id, icon, msg[0], msg[1:], [], hint, timeout )
handler = DbusNotificationHandler()
notify = handler.notification
msg = [ "Daemon Started", "Daemon is now running - %s"%os.getpid() ]
notify(msg)
temp = "95 Celsius"
msg = [ "High Temp Warning", "CPU temperature has reached %s"%temp ]
notify(msg, 'warn')
You can set DBUS_SESSION_BUS_ADDRESS environment variable to choose the dbus session you want to connect to.
Incorrect permissions (i.e., missing the seteuid) causes an immediate NoReply, and not defining DBUS_SESSION_BUS_ADDRESS responded with Using X11 for dbus-daemon autolaunch was disabled at compile time, set your DBUS_SESSION_BUS_ADDRESS instead.
Here's the test code I used:
import os
import dbus
# become user
uid = os.environ["SUDO_UID"]
print(f"I'm {os.geteuid()}, becoming {uid}")
os.seteuid(int(uid))
# set the dbus address
os.environ["DBUS_SESSION_BUS_ADDRESS"] = f"unix:path=/run/user/{uid}/bus"
bus = dbus.SessionBus()
print(bus.list_names())
# I'm 0, becoming 1000
# dbus.Array([dbus.String('org.freedesktop.DBus'), dbus.String('org.fr .. <snip>

How to establish bluetooth HFP service level connection

I am trying to establish a HFP (Hands Free Profile) service level connection using the python test code provided in the bluez repository.
In order to even run, the example needed some modifications:
Fix the deprecated import statement:
import glib
try:
from gi.repository import GObject
except ImportError:
import gobject as GObject
=>
from gi.repository import GLib as glib
Change the mainloop object:
mainloop = GObject.MainLoop()
=>
mainloop = glib.MainLoop()
Disable audio since it is not relevant to this test:
audio_supported = False
Encode commands sent to bytes:
os.write(self.fd, cmd + "\r\n")
=>
os.write(self.fd, f"{cmd}\r\n".encode())
Decode bytes received from buffer:
buf = buf.strip()
=>
buf = buf.decode('utf8').strip()
Change BDADDR_ANY to the phone's address:
At this point the example runs, in order to actually attempt a connection I added the following code:
fd = os.open("test.log", os.O_RDWR)
profile.NewConnection(options.path, fd, opts)
At this point I get an error from NewConnection() at the following line: fd = fd.take()
Looking at the code, fd seems to be a simple file descriptor so commenting out this line helps me move on.
The actual problem that I am facing now is that I get no reply from the device after sending the initial AT command (line 78 from the link above). As per the Bluetooth HFP 1.8 spec this is supposed to start the service level connection procedure. I am not even sure that my command is making it to the device.
I also tried another example which is a bit newer. This one is able to open a RFCOMM connection through a socket (BluetoothSocket) but the connection keeps getting reset. This is because the _read_at() function (line 181) does not receive anything from the device and times out.

Selenium WebDriver + Tor as proxy with Stem?

I need to confirm If Stem can be used to launch a Tor process that exposes 127.0.0.1:port, then use it on selenium scripts as proxy (SOCKS).
I'm using Python 3.4.2 , Stem 1.3.0, and Tor (tor-win32-tor-0.2.5.10 expert
bundle) on Windows.
This piece of code works with a standard SOCKS proxy.
from selenium import webdriver
from selenium.webdriver.firefox.firefox_profile import FirefoxProfile
profile = FirefoxProfile()
profile.set_preference('network.proxy.type', 1)
profile.set_preference('network.proxy.socks', '127.0.0.1')
profile.set_preference('network.proxy.socks_port', 9000)
driver = webdriver.Firefox(profile)
driver.implicitly_wait(30)
driver.get('http://www.reddit.com')
But I can't manage to get it working with Tor as my proxy. I tried to create a Tor process, and its created. But I don't really know If it's working properly. I don't get errors in my tor_error_log.txt
# File: stem_process.py
import stem.process
import stem
stem.process.launch_tor_with_config(
config = {
'SocksPort': '9000',
'ControlPort': '9051',
'ExitNodes': '{us}',
'Log': [
'NOTICE stdout',
'ERR file c:\\tor-win32-tor-0.2.5.10\\Tor\\tor_error_log.txt',
],
},
tor_cmd = 'C:\\tor-win32-tor-0.2.5.10\\Tor\\tor.exe',
)
Then I tried two ways to create the connection or authenticate. The first one is using with and stem.control.controller. And the second at lower level with stem.socket and stem.connection
The first one:
# File: stem_test1.py
from stem.control import Controller
with Controller.from_port(address='127.0.0.1', port=9051) as controller: #port = 9051
controller.authenticate()
print("Tor is running version %s" % controller.get_version())
'''
# Output:
Tor is running version 0.2.5.10 (git-13318a95ddfbbf8d)
'''
The second one:
# File: stem_test2.py
import sys
import stem
import stem.connection
import stem.socket
if __name__ == '__main__':
try:
control_socket = stem.socket.ControlPort(port = 9051)
stem.connection.authenticate(control_socket)
except stem.SocketError as exc:
print('Unable to connect to tor on port 9051: %s' % exc)
sys.exit(1)
except stem.connection.AuthenticationFailure as exc:
print('Unable to authenticate: %s' % exc)
sys.exit(1)
print("Issuing 'GETINFO version' query...\n")
control_socket.send('GETINFO version')
print(control_socket.recv())
'''
# Output:
Issuing 'GETINFO version' query...
version=0.2.5.10 (git-13318a95ddfbbf8d)
OK
'''
And both run without errors... But when I use the code to call the Firefox WebDriver instance with 127.0.0.1:9000 as proxy (also tried with 127.0.0.1:9051, because I don't really know the difference between socksPort and controlPort) It doesn't work.
Stem can't create a tor process, its only a library for connecting to an existing tor server for inspection/control via the control port.
To create the tor process itself, you need to have your system start it up with upstart/launchctl/etc. Alternatively, you can just type tor from the commandline if its installed and it'll run in the foreground.
With that, to use stem you'll need to edit your torrc to a. enable the ControlPort, and b. set an authentication method (cookieauth or hashed password stored in your torrc). The default tor SocksPort is 9050 and ControlPort is 9051.
The SocksPort is the one you route your traffic (i.e. firefox) through, the ControlPort is what you connect stem to.
Mind you, only if you even need stem, since it just seems like you're trying to start a tor instance with it (and thats not do-able), if you get it running on your system vanilla, it'll work with selenium/firefox as you've got it configured (well, default port is 9050 not 9000)

Fetching xmpp resource string using pidgin dbus python api

I was using the pidgin dbus api to print the names of my gtalk buddies and their status by writing the following python code snippet:
import dbus
# Initiate a connection to the Session Bus
bus = dbus.SessionBus()
# Associate Pidgin's D-Bus interface with Python objects
obj = bus.get_object("im.pidgin.purple.PurpleService", "/im/pidgin/purple/PurpleObject")
purple = dbus.Interface(obj, "im.pidgin.purple.PurpleInterface")
# Iterate through every active account
for acctID in purple.PurpleAccountsGetAllActive():
for buddy in purple.PurpleFindBuddies(acctID,""):
print purple.PurpleBuddyGetName(buddy),'Online' if purple.PurpleBuddyIsOnline(buddy) else 'Offline'
In Pidgin when i hover my mouse over a particular buddy, it also shows the Resource string of that buddy for example gtalk, android etc... which tells me which resource the user is logged in from.
Is there a way to fetch this Resource string using pidgins dbus api or some other way ?
Please Help
Thank You
you can use the script from the wiki page:
#!/usr/bin/env python
def my_func(account, sender, message, conversation, flags):
print sender, "said:", message
import dbus, gobject
from dbus.mainloop.glib import DBusGMainLoop
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
bus = dbus.SessionBus()
bus.add_signal_receiver(my_func,
dbus_interface="im.pidgin.purple.PurpleInterface",
signal_name="ReceivedImMsg")
loop = gobject.MainLoop()
loop.run()
sender here will be smth like this:
'example#gmail.com/androidXXXXXXXX' in case of using android or 'example#gmail.com/gmail.XXXXXXXX' for gtalk or
'example#gmail.com/XXXXXXXX' for other im, where X is a hex value.

twisted dns doesn't work

I'd like to know why the following doesn't work.
from twisted internet import defer, reactor
from twisted.python.failure import Failure
import twisted.names.client
def do_lookup(do_lookup):
d = twisted.names.client.getHostByName(domain)
d.addBoth(lookup_done)
def lookup_done(result):
print 'result:', result
reactor.stop()
domain = 'twistedmatrix.com'
reactor.callLater(0, do_lookup, domain)
reactor.run()
Results in:
result: [Failure instance: Traceback
(failure with no frames): <class
'twisted.names.error.ResolverError'>:
Stuck at response without answers or
delegation ]
As of this writing, this fails on Windows, since it uses an invalid path for the windows host file (in twisted.names.client.createResolver. It uses 'c:\windows\hosts'. This was fine for windows versions 98 and Me (reference here), but would fail with a version as "modern" as XP.
Today, it should probably use something like:
hosts = os.path.join(
os.environ.get('systemroot','C:\\Windows'),
r'system32\drivers\etc\hosts'
)
I think this only partly resolves the issue though (or maybe this is a red herring).
This will only work now for names actually specified in this hosts file. What it likely needs to do is some sort of registry query for the DNS server, then query that for the actual DNS name lookup.
This recipe looks promising for getting the actual DNS server.
Correcting your example to the following, so that it is syntactically valid:
from twisted.internet import reactor
import twisted.names.client
def do_lookup(domain):
d = twisted.names.client.getHostByName(domain)
d.addBoth(lookup_done)
def lookup_done(result):
print 'result:', result
reactor.stop()
domain = 'twistedmatrix.com'
reactor.callLater(0, do_lookup, domain)
reactor.run()
I get:
$ python so-example.py
result: 66.35.39.65
So, to answer your question: your local DNS environment is broken, not twisted.names. Or maybe there's a bug. You'll need to track it down further.
I did some digging why an explicit client.createResolver(servers) wasn't working on our corporate windows machines. In the guts of Twisted's createResolver is a os-dependant branch:
def createResolver(servers=None, resolvconf=None, hosts=None):
...
from twisted.names import resolve, cache, root, hosts as hostsModule
if platform.getType() == 'posix':
if resolvconf is None:
resolvconf = '/etc/resolv.conf'
if hosts is None:
hosts = '/etc/hosts'
theResolver = Resolver(resolvconf, servers)
hostResolver = hostsModule.Resolver(hosts)
else:
if hosts is None:
hosts = r'c:\windows\hosts'
from twisted.internet import reactor
bootstrap = _ThreadedResolverImpl(reactor)
hostResolver = hostsModule.Resolver(hosts)
theResolver = root.bootstrap(bootstrap)
L = [hostResolver, cache.CacheResolver(), theResolver]
return resolve.ResolverChain(L)
The first warning sign for Windows is that argument servers is not used, so custom-DNS servers are ignored. Next is that the _ThreadedResolverImpl(), which uses platform specific code, is wrapped in a root.bootstrap() before being added to the resolver chain.
The purpose of root.bootstrap is to use the platform resolver to lookup a.root-servers.net, b.root-servers.net, etc. using the synchronous Windows platform resolver (which works - and returns IPs), and then do direct DNS queries against the root servers. The UDP packets fired off to root servers are then blocked by our corporate firewall - I see them in Wireshark.
The default getResolver() call used by names.client.getHostByName() invokes createResolver() directly, which may again result in this broken setup bootstrapped by a working DNS setup. For most environments I think adding the Resolver (with root or explicit servers) into the ResolverChain along with _ThreadedResolverImpl as a fallback would work - except that the platform resolver is a different interface.
Here's an example of detecting the local DNS servers asynchronously (by parsing the output of ipconfig) then installing a custom resolver to use them.
import sys
from twisted.python import log
from twisted.names import root, hosts, resolve, cache, client
from twisted.python.runtime import platform
from twisted.internet import reactor
from twisted.internet import utils
import re
def parseIpconfigDNSServers(output):
servers = []
found = False
for line in output.split('\n'):
if 'DNS Servers . . . . . . . . . . . :' in line or (found and not '. . . .' in line):
servers.append(line[38:].strip())
found = True
else:
found = False
log.msg( 'Windows: Detected DNS servers %s' % (str(servers)))
return servers
if platform.getType() != 'posix':
d = utils.getProcessOutput(os.path.join(os.environ['WINDIR'], 'system32', 'ipconfig.exe'), ["/all"])
d.addCallback(parseIpconfigDNSServers)
d.addCallback(lambda r: client.Resolver(servers=[(h, 53) for h in r]))
d.addErrback(log.msg)
theResolver = root.DeferredResolver(d)
client.theResolver = resolve.ResolverChain([cache.CacheResolver(), theResolver])
if __name__ == '__main__':
log.startLogging(sys.stdout)
def do_lookup(domain):
d = client.getHostByName(domain)
d.addBoth(log.msg)
from twisted.internet import reactor
reactor.callLater(0, do_lookup, 'example.com')
reactor.run()
I've tidied this up and posted it here https://gist.github.com/shuckc/af7490e1c4a2652ca740

Categories

Resources