Skip to content

Commit

Permalink
Fix D-Bus name requests not raising appropriate exceptions
Browse files Browse the repository at this point in the history
`SdBusRequestNameExistsError`: Someone already owns name
`SdBusRequestNameAlreadyOwnerError`: Caller already owns name
`SdBusRequestNameInQueueError`: Name request queued up
  • Loading branch information
igo95862 committed Jan 14, 2023
1 parent 6a7264f commit a8bcbd2
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 13 deletions.
61 changes: 48 additions & 13 deletions src/sdbus/sd_bus_internals_bus.c
Original file line number Diff line number Diff line change
Expand Up @@ -468,9 +468,9 @@ static PyObject* SdBus_get_signal_queue(SdBusObject* self, PyObject* args) {
return new_future;
}

int SdBus_request_callback(sd_bus_message* m,
void* userdata, // Should be the asyncio.Future
sd_bus_error* Py_UNUSED(ret_error)) {
int SdBus_request_name_callback(sd_bus_message* m,
void* userdata, // Should be the asyncio.Future
sd_bus_error* Py_UNUSED(ret_error)) {
PyObject* py_future = userdata;
PyObject* is_cancelled CLEANUP_PY_OBJECT = PyObject_CallMethod(py_future, "cancelled", "");
if (Py_True == is_cancelled) {
Expand All @@ -479,11 +479,31 @@ int SdBus_request_callback(sd_bus_message* m,
}

if (!sd_bus_message_is_method_error(m, NULL)) {
// Not Error, set Future result to new message object
PyObject* return_object CLEANUP_PY_OBJECT = PyObject_CallMethod(py_future, "set_result", "O", Py_None);
if (return_object == NULL) {
return -1;
uint32_t request_name_result = 0;
CALL_SD_BUS_CHECK_RETURN_NEG1(sd_bus_message_read_basic(m, 'u', &request_name_result));
if (1 == request_name_result) {
// Successfully acquired the name
Py_XDECREF(CALL_PYTHON_CHECK_RETURN_NEG1(PyObject_CallMethod(py_future, "set_result", "O", Py_None)));
return 0;
}

PyObject* exception_to_raise CLEANUP_PY_OBJECT = NULL;
switch (request_name_result) {
case 2:
exception_to_raise = CALL_PYTHON_CHECK_RETURN_NEG1(PyObject_CallFunctionObjArgs(exception_request_name_in_queue, NULL));
break;
case 3:
exception_to_raise = CALL_PYTHON_CHECK_RETURN_NEG1(PyObject_CallFunctionObjArgs(exception_request_name_exists, NULL));
break;
case 4:
exception_to_raise = CALL_PYTHON_CHECK_RETURN_NEG1(PyObject_CallFunctionObjArgs(exception_request_name_already_owner, NULL));
break;
default:
exception_to_raise = CALL_PYTHON_CHECK_RETURN_NEG1(PyObject_CallFunctionObjArgs(exception_request_name, NULL));
break;
}
Py_XDECREF(CALL_PYTHON_CHECK_RETURN_NEG1(PyObject_CallMethod(py_future, "set_exception", "O", exception_to_raise)));
return -1;
} else {
// An Error, set exception
if (future_set_exception_from_message(py_future, m) < 0) {
Expand Down Expand Up @@ -517,11 +537,9 @@ static PyObject* SdBus_request_name_async(SdBusObject* self, PyObject* args) {
SdBusSlotObject* new_slot_object CLEANUP_SD_BUS_SLOT = (SdBusSlotObject*)CALL_PYTHON_AND_CHECK(SD_BUS_PY_CLASS_DUNDER_NEW(SdBusSlot_class));

CALL_SD_BUS_AND_CHECK(
sd_bus_request_name_async(self->sd_bus_ref, &new_slot_object->slot_ref, service_name_char_ptr, flags, SdBus_request_callback, new_future));
sd_bus_request_name_async(self->sd_bus_ref, &new_slot_object->slot_ref, service_name_char_ptr, flags, SdBus_request_name_callback, new_future));

if (PyObject_SetAttrString(new_future, "_sd_bus_py_slot", (PyObject*)new_slot_object) < 0) {
return NULL;
}
CALL_PYTHON_INT_CHECK(PyObject_SetAttrString(new_future, "_sd_bus_py_slot", (PyObject*)new_slot_object));
CHECK_SD_BUS_READER;
return new_future;
}
Expand All @@ -544,8 +562,25 @@ static PyObject* SdBus_request_name(SdBusObject* self, PyObject* args) {
CALL_PYTHON_BOOL_CHECK(PyArg_ParseTuple(args, "sK", &service_name_char_ptr, &flags_long_long, NULL));
uint64_t flags = (uint64_t)flags_long_long;
#endif
CALL_SD_BUS_AND_CHECK(sd_bus_request_name(self->sd_bus_ref, service_name_char_ptr, flags));
Py_RETURN_NONE;
int request_name_return_code = sd_bus_request_name(self->sd_bus_ref, service_name_char_ptr, flags);
switch (request_name_return_code) {
case -EEXIST:
return PyErr_Format(exception_request_name_exists, "Name \"%s\" already owned.", service_name_char_ptr, NULL);
break;
case -EALREADY:
return PyErr_Format(exception_request_name_already_owner, "Already own name \"%s\".", service_name_char_ptr, NULL);
break;
case 0:
return PyErr_Format(exception_request_name_in_queue, "Queued up to acquire name \"%s\".", service_name_char_ptr, NULL);
break;
case 1:
Py_RETURN_NONE;
break;
default:
CALL_SD_BUS_AND_CHECK(request_name_return_code);
break;
}
Py_UNREACHABLE();
}

#ifndef Py_LIMITED_API
Expand Down
82 changes: 82 additions & 0 deletions test/test_request_name.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,24 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
from __future__ import annotations

from asyncio import wait_for
from unittest import main

from sdbus.exceptions import (
SdBusLibraryError,
SdBusRequestNameAlreadyOwnerError,
SdBusRequestNameError,
SdBusRequestNameExistsError,
SdBusRequestNameInQueueError,
)
from sdbus.sd_bus_internals import NameQueueFlag
from sdbus.unittest import IsolatedDbusTestCase

from sdbus import sd_bus_open_user

TEST_BUS_NAME = 'com.example.test'
TEST_BUS_NAME_regex_match = TEST_BUS_NAME.replace('.', r'\.')


class TestRequestName(IsolatedDbusTestCase):
async def asyncSetUp(self) -> None:
Expand Down Expand Up @@ -75,6 +83,80 @@ def test_request_name_exception_tree(self) -> None:
)
)

async def test_name_exists_async(self) -> None:
extra_bus = sd_bus_open_user()
await self.bus.request_name_async(TEST_BUS_NAME, 0)

with self.assertRaises(SdBusRequestNameExistsError):
await wait_for(
extra_bus.request_name_async(TEST_BUS_NAME, 0),
timeout=1,
)

async def test_name_already_async(self) -> None:
await self.bus.request_name_async(TEST_BUS_NAME, 0)

with self.assertRaises(SdBusRequestNameAlreadyOwnerError):
await wait_for(
self.bus.request_name_async(TEST_BUS_NAME, 0),
timeout=1,
)

async def test_name_queued_async(self) -> None:
extra_bus = sd_bus_open_user()
await self.bus.request_name_async(TEST_BUS_NAME, 0)

with self.assertRaises(SdBusRequestNameInQueueError):
await wait_for(
extra_bus.request_name_async(TEST_BUS_NAME, NameQueueFlag),
timeout=1,
)

async def test_name_other_error_async(self) -> None:
extra_bus = sd_bus_open_user()
extra_bus.close()

with self.assertRaises(SdBusLibraryError):
await wait_for(
extra_bus.request_name_async(TEST_BUS_NAME, 0),
timeout=1,
)

def test_name_exists_block(self) -> None:
extra_bus = sd_bus_open_user()
self.bus.request_name(TEST_BUS_NAME, 0)

with self.assertRaisesRegex(
SdBusRequestNameExistsError,
TEST_BUS_NAME_regex_match,
):
extra_bus.request_name(TEST_BUS_NAME, 0)

def test_name_already_block(self) -> None:
self.bus.request_name(TEST_BUS_NAME, 0)

with self.assertRaisesRegex(
SdBusRequestNameAlreadyOwnerError,
TEST_BUS_NAME_regex_match,
):
self.bus.request_name(TEST_BUS_NAME, 0)

def test_name_queued_block(self) -> None:
extra_bus = sd_bus_open_user()
self.bus.request_name(TEST_BUS_NAME, 0)

with self.assertRaisesRegex(
SdBusRequestNameInQueueError,
TEST_BUS_NAME_regex_match,
):
extra_bus.request_name(TEST_BUS_NAME, NameQueueFlag)

def test_name_other_error_block(self) -> None:
extra_bus = sd_bus_open_user()
extra_bus.close()
with self.assertRaises(SdBusLibraryError):
extra_bus.request_name(TEST_BUS_NAME, 0)


if __name__ == '__main__':
main()

0 comments on commit a8bcbd2

Please sign in to comment.