DBUS communication between two users

Viewed 825

Unfortunately, there is very little information about a dbus communication. Does anyone have an idea, how to accomplish communication between two users on the same linux? What things should be pay attention to? What type of dbus (session or system) should be use?

2 Answers

If you intend to use two Linux users for your client-server application, the service needs to request a system bus.

What you need to pay attention to is the dbusconfig should be installed/placed under /etc/dbus-1/system.d/your-bus-name.conf and define which user can own the bus, which user can communicate to the bus, receive information from the sender.

e.g.

<!DOCTYPE busconfig PUBLIC
          "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
          "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">

<!-- To be installed to /etc/dbus-1/system.d/com.test.DBus.conf so your service can own a system bus -->

<busconfig>
  <policy user="root">
    <allow own="com.test.DBus"/>
  </policy>
  <policy user="user">
    <allow own="com.test.DBus"/>
  </policy>

  <!-- Allow anyone to invoke methods to the interface -->
  <policy context="default">
    <allow send_destination="com.test.DBus"/>
    <allow receive_sender="com.test.DBus"/>
  </policy>
</busconfig>

I assume you already have some client-server applications that can communicate over D-Bus.

Thanks, @Anton, I now understand that the session bus must to be satisfy two conditions: the same uid and the same session bus. Currently, I try to establish communication between two users via system D-bus, but obviously I'm doing wrong.

/etc/dbus-1/system.d/test_bus.conf

<!DOCTYPE busconfig PUBLIC
 "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
 "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
        <policy user="user">
                <allow own="com.test.Bus"/>
                <allow send_destination="com.test.Bus"/>
                <allow send_interface="com.test.Bus"/>
                <allow receive_sender="com.test.Bus"/>
        </policy>
        <policy user="user2">
                <allow own="com.test.Bus"/>
                <allow send_destination="com.test.Bus"/>
                <allow send_interface="com.test.Bus"/>
                <allow receive_sender="com.test.Bus"/>
        </policy>
</busconfig>

server.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <ctype.h>
#include <dbus/dbus.h>

const char* const INTERFACE_NAME = "com.test.Bus";
const char* const SERVER_BUS_NAME = "com.test.Bus";
const char* const OBJECT_PATH_NAME = "/server";
const char* const METHOD_NAME = "add_numbers";

DBusError dbus_error;
void      print_dbus_error(char* str);
bool      isinteger(char* ptr);

int main(int argc, char** argv)
{

    DBusConnection* conn;
    int             ret;

    dbus_error_init(&dbus_error);

    conn = dbus_bus_get(DBUS_BUS_SYSTEM, &dbus_error);
    printf("Con name=%s\n", dbus_bus_get_unique_name(conn)); // my

    if (dbus_error_is_set(&dbus_error))
        print_dbus_error("dbus_bus_get");

    if (!conn)
        exit(1);

    // Get a well known name
    ret = dbus_bus_request_name(conn, SERVER_BUS_NAME, DBUS_NAME_FLAG_DO_NOT_QUEUE, &dbus_error);

    if (dbus_error_is_set(&dbus_error))
        print_dbus_error("dbus_bus_get");

    if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
    {
        fprintf(stderr, "Dbus: not primary owner, ret = %d\n", ret);
        exit(1);
    }

    // Handle request from clients
    while (1)
    {
        // Block for msg from client
        if (!dbus_connection_read_write_dispatch(conn, -1))
        {
            fprintf(stderr, "Not connected now.\n");
            exit(1);
        }

        DBusMessage* message;

        if ((message = dbus_connection_pop_message(conn)) == NULL)
        {
            fprintf(stderr, "Did not get message\n");
            continue;
        }

        if (dbus_message_is_method_call(message, INTERFACE_NAME, METHOD_NAME))
        {
            char*      s;
            char *     str1 = NULL, *str2 = NULL;
            const char space[4] = " \n\t";
            long       i, j;
            bool       error = false;

            if (dbus_message_get_args(message, &dbus_error, DBUS_TYPE_STRING, &s, DBUS_TYPE_INVALID))
            {
                printf("%s", s);
                // Validate received message
                str1 = strtok(s, space);
                if (str1)
                    str2 = strtok(NULL, space);

                if (!str1 || !str2)
                    error = true;

                if (!error)
                {
                    if (isinteger(str1))
                        i = atol(str1);
                    else
                        error = true;
                }
                if (!error)
                {
                    if (isinteger(str2))
                        j = atol(str2);
                    else
                        error = true;
                }

                if (!error)
                {
                    // send reply
                    DBusMessage* reply;
                    char         answer[40];

                    sprintf(answer, "Sum is %ld", i + j);
                    if ((reply = dbus_message_new_method_return(message)) == NULL)
                    {
                        fprintf(stderr, "Error in dbus_message_new_method_return\n");
                        exit(1);
                    }

                    DBusMessageIter iter;
                    dbus_message_iter_init_append(reply, &iter);
                    char* ptr = answer;
                    if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &ptr))
                    {
                        fprintf(stderr, "Error in dbus_message_iter_append_basic\n");
                        exit(1);
                    }

                    if (!dbus_connection_send(conn, reply, NULL))
                    {
                        fprintf(stderr, "Error in dbus_connection_send\n");
                        exit(1);
                    }

                    dbus_connection_flush(conn);

                    dbus_message_unref(reply);
                }
                else // There was an error
                {
                    DBusMessage* dbus_error_msg;
                    char         error_msg[] = "Error in input";
                    if ((dbus_error_msg = dbus_message_new_error(message, DBUS_ERROR_FAILED, error_msg)) == NULL)
                    {
                        fprintf(stderr, "Error in dbus_message_new_error\n");
                        exit(1);
                    }

                    if (!dbus_connection_send(conn, dbus_error_msg, NULL))
                    {
                        fprintf(stderr, "Error in dbus_connection_send\n");
                        exit(1);
                    }

                    dbus_connection_flush(conn);

                    dbus_message_unref(dbus_error_msg);
                }
            }
            else
            {
                print_dbus_error("Error getting message");
            }
        }
    }

    return 0;
}

bool isinteger(char* ptr)
{
    if (*ptr == '+' || *ptr == '-')
        ptr++;

    while (*ptr)
    {
        if (!isdigit((int)*ptr++))
            return false;
    }

    return true;
}

void print_dbus_error(char* str)
{
    fprintf(stderr, "%s: %s\n", str, dbus_error.message);
    dbus_error_free(&dbus_error);
}

client.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <ctype.h>

#include <dbus/dbus.h>

const char* const INTERFACE_NAME = "com.test.Bus";
const char* const SERVER_BUS_NAME = "com.test.Bus";
const char* const CLIENT_BUS_NAME = "com.test.Bus";
const char* const SERVER_OBJECT_PATH_NAME = "/server";
const char* const CLIENT_OBJECT_PATH_NAME = "/client";
const char* const METHOD_NAME = "add_numbers";

DBusError dbus_error;
void      print_dbus_error(char* str);

int main(int argc, char** argv)
{


    DBusConnection* conn;
    int             ret;
    char            input[80];

    dbus_error_init(&dbus_error);

    conn = dbus_bus_get(DBUS_BUS_SYSTEM, &dbus_error);

    printf("Con name=%s\n", dbus_bus_get_unique_name(conn)); // my

    if (dbus_error_is_set(&dbus_error))
        print_dbus_error("dbus_bus_get");

    if (!conn)
        exit(1);

    printf("Please type two numbers: ");
    while (fgets(input, 78, stdin) != NULL)
    {
        // Get a well known name
        while (1)
        {
            ret = dbus_bus_request_name(conn, CLIENT_BUS_NAME, 0, &dbus_error);

            if (ret == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
                break;

            if (ret == DBUS_REQUEST_NAME_REPLY_IN_QUEUE)
            {
                fprintf(stderr, "Waiting for the bus ... \n");
                sleep(1);
                continue;
            }
            if (dbus_error_is_set(&dbus_error))
                print_dbus_error("dbus_bus_get");
        }

        DBusMessage* request;

        if ((request = dbus_message_new_method_call(SERVER_BUS_NAME, SERVER_OBJECT_PATH_NAME, INTERFACE_NAME,
                                                    METHOD_NAME)) == NULL)
        {
            fprintf(stderr, "Error in dbus_message_new_method_call\n");
            exit(1);
        }

        DBusMessageIter iter;
        dbus_message_iter_init_append(request, &iter);
        char* ptr = input;
        if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &ptr))
        {
            fprintf(stderr, "Error in dbus_message_iter_append_basic\n");
            exit(1);
        }
        DBusPendingCall* pending_return;
        if (!dbus_connection_send_with_reply(conn, request, &pending_return, -1))
        {
            fprintf(stderr, "Error in dbus_connection_send_with_reply\n");
            exit(1);
        }

        if (pending_return == NULL)
        {
            fprintf(stderr, "pending return is NULL");
            exit(1);
        }

        dbus_connection_flush(conn);

        dbus_message_unref(request);

        dbus_pending_call_block(pending_return);

        DBusMessage* reply;
        if ((reply = dbus_pending_call_steal_reply(pending_return)) == NULL)
        {
            fprintf(stderr, "Error in dbus_pending_call_steal_reply");
            exit(1);
        }

        dbus_pending_call_unref(pending_return);

        char* s;
        if (dbus_message_get_args(reply, &dbus_error, DBUS_TYPE_STRING, &s, DBUS_TYPE_INVALID))
        {
            printf("%s\n", s);
        }
        else
        {
            fprintf(stderr, "Did not get arguments in reply\n");
            exit(1);
        }
        dbus_message_unref(reply);

        if (dbus_bus_release_name(conn, CLIENT_BUS_NAME, &dbus_error) == -1)
        {
            fprintf(stderr, "Error in dbus_bus_release_name\n");
            exit(1);
        }

        printf("Please type two numbers: ");
    }

    return 0;
}

void print_dbus_error(char* str)
{
    fprintf(stderr, "%s: %s\n", str, dbus_error.message);
    dbus_error_free(&dbus_error);
}

Makefile

all: serverDBUS clientDBUS

%.o: %.c
    gcc -Wall -c $< `pkg-config --cflags dbus-1`

serverDBUS: server.o
    gcc server.o -o serverDBUS `pkg-config --libs dbus-1`

clientDBUS: client.o
    gcc client.o -o clientDBUS `pkg-config --libs dbus-1`

.PHONY: clean
clean:
    rm *.o serverDBUS clientDBUS
Related