Skip to content

Commit

Permalink
Merge pull request #3367 from candlepin/jhnidek/1.28_rhel-21049
Browse files Browse the repository at this point in the history
RHEL-21049: [1.28] Fix issue with registration using gsd-subman
  • Loading branch information
m-horky authored Jan 16, 2024
2 parents 0da6591 + 20aecc5 commit c6a6cc1
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 11 deletions.
15 changes: 15 additions & 0 deletions etc-conf/dbus/system.d/com.redhat.RHSM1.conf
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,21 @@
send_interface="com.redhat.RHSM1.Config"
send_member="Get"/>

<!--
Non-root user can create abstract socket with Start()
method and only root user or user with same UID can
use this socket. Only root user can use such socket
for calling Register() on interface
com.redhat.RHSM1.Register
-->
<allow send_destination="com.redhat.RHSM1"
send_interface="com.redhat.RHSM1.RegisterServer"
send_member="Start"/>

<allow send_destination="com.redhat.RHSM1"
send_interface="com.redhat.RHSM1.RegisterServer"
send_member="Stop"/>

<!--
The UUID returned by following method is read
from consumer cert. Only this file is not
Expand Down
7 changes: 7 additions & 0 deletions src/rhsmlib/dbus/dbus_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ def pid_of_sender(bus, sender):
pid = int(dbus_iface.GetConnectionUnixProcessID(sender))
except ValueError:
return None
# It seems that Python D-Bus implementation contains error. This should not happen,
# when we try to call GetConnectionUnixProcessID() with sender that does not exist
# anymore
except dbus.exceptions.DBusException as err:
log.debug(f"D-Bus raised exception: {err}")
return None

return pid


Expand Down
28 changes: 21 additions & 7 deletions src/rhsmlib/dbus/objects/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,13 @@ def Start(self, locale, sender=None):
Locale.set(locale)

with self.lock:
if self.server:
# When some other application already started domain socket listener, then
# write log message and return existing address
if self.server is not None:
log.debug(f"Domain socket listener already running, started by: {self.server.sender}")
# Add sender to the list of senders using server
log.debug(f"Adding another sender {sender} to the set of senders")
self.server.add_sender(sender)
return self.server.address

log.debug('Attempting to create new domain socket server')
Expand All @@ -80,14 +86,22 @@ def Stop(self, locale, sender=None):
locale = dbus_utils.dbus_to_python(locale, expected_type=str)
Locale.set(locale)
with self.lock:
if self.server:
self.server.shutdown()
self.server = None
log.debug("Stopped DomainSocketServer")
return True
else:
if self.server is None:
raise exceptions.Failed("No domain socket server is running")

# Remove current sender and check if other senders are still running.
# If there is at least one sender using this server still running, then
# only return False
self.server.remove_sender(sender)
if self.server.are_other_senders_still_running() is True:
log.debug("Not stopping domain socket server, because some senders still uses it.")
return False

self.server.shutdown()
self.server = None
log.debug("Domain socket server stopped.")
return True


class OrgNotSpecifiedException(dbus.DBusException):
"""
Expand Down
43 changes: 42 additions & 1 deletion src/rhsmlib/dbus/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from subscription_manager.ga import GLib
from functools import partial
from rhsmlib.services import config
from rhsmlib.dbus.dbus_utils import pid_of_sender
from rhsm.config import get_config_parser
from rhsmlib.file_monitor import create_filesystem_watcher, DirectoryWatch
from rhsmlib.file_monitor import CONSUMER_WATCHER, ENTITLEMENT_WATCHER, CONFIG_WATCHER, PRODUCT_WATCHER, \
Expand Down Expand Up @@ -310,13 +311,53 @@ def __init__(self, object_classes=None, sender=None, cmd_line=None):
self.object_classes = object_classes or []
self.objects = []
self._server = None
self.sender = sender
self.sender = sender # sender created the server
self._senders = set() # other senders using server
log.debug(f"Adding sender {sender} to the set of senders")
self._senders.add(sender)
self.cmd_line = cmd_line

self.lock = threading.Lock()
with self.lock:
self.connection_count = 0

def add_sender(self, sender: str) -> None:
"""
Add sender to the list of senders
"""
self._senders.add(sender)

def remove_sender(self, sender: str) -> bool:
"""
Try to remove sender from the set of sender
"""
try:
self._senders.remove(sender)
except KeyError:
log.debug(f"Sender {sender} wasn't removed from the set of senders (not member of the set)")
return False
else:
log.debug(f"Sender {sender} removed from the set of senders")
return True

def are_other_senders_still_running(self) -> bool:
"""
Check if other users are still running. It sender in the set is not
running, then remove sender from the set, because sender could
crash, or it was terminated since it called Start() method.
"""
is_one_sender_running = False
not_running = set()
bus = dbus.SystemBus()
for sender in self._senders:
pid = pid_of_sender(bus, sender)
if pid is None:
not_running.add(sender)
else:
is_one_sender_running = True
self._senders = self._senders.difference(not_running)
return is_one_sender_running

def shutdown(self):
for o in self.objects:
o.remove_from_connection()
Expand Down
20 changes: 17 additions & 3 deletions test/rhsmlib_test/test_register.py
Original file line number Diff line number Diff line change
Expand Up @@ -758,12 +758,26 @@ def setUp(self) -> None:
socket_iface_patch = mock.patch.object(DomainSocketServer, "_server_socket_iface", "unix:dir=")
socket_iface_patch.start()

pid_of_sender_patch = mock.patch(
"rhsmlib.dbus.server.pid_of_sender",
autospec=True,
)
pid_of_sender_mock = pid_of_sender_patch.start()
pid_of_sender_mock.return_value = 123456

are_others_running_path = mock.patch(
"rhsmlib.dbus.server.DomainSocketServer.are_other_senders_still_running",
autospec=True,
)
are_others_running_mock = are_others_running_path.start()
are_others_running_mock.return_value = False

super().setUp()

def tearDown(self) -> None:
"""Make sure the domain server is stopped once the test ends."""
with contextlib.suppress(rhsmlib.dbus.exceptions.Failed, rhsmlib.dbus.exceptions.RHSM1DBusException):
self.obj.Stop.__wrapped__(self.obj, self.LOCALE)
self.obj.Stop.__wrapped__(self.obj, self.LOCALE, "sender")

super().tearDown()

Expand Down Expand Up @@ -791,11 +805,11 @@ def test_Start__can_connect(self):

def test_Stop(self):
self.obj.Start.__wrapped__(self.obj, self.LOCALE)
self.obj.Stop.__wrapped__(self.obj, self.LOCALE)
self.obj.Stop.__wrapped__(self.obj, self.LOCALE, "sender")

def test_Stop__not_running(self):
try:
self.obj.Stop.__wrapped__(self.obj, self.LOCALE)
self.obj.Stop.__wrapped__(self.obj, self.LOCALE, "sender")
except (rhsmlib.dbus.exceptions.RHSM1DBusException, rhsmlib.dbus.exceptions.Failed):
return
raise pytest.fail("Stop did not raise an exception")
Expand Down

0 comments on commit c6a6cc1

Please sign in to comment.