I want to send notifications to my window manager on linux through the org.freedesktop.Notifications service. In d-feet this works, but in C, I get the error "Connection was disconnected before a reply was received"
#include <stdio.h>
#include <dbus/dbus.h>
int main() {
DBusError error;
DBusConnection *conn;
dbus_uint32_t serial = 0;
char *app_name = "My App";
dbus_uint32_t replaces_id = 0;
char *app_icon = "";
char *summary = "Notification from My App";
char *body = "This is a test notification from My App.";
char **actions = NULL;
dbus_int32_t timeout = -1;
dbus_uint32_t notification_id = 0;
dbus_error_init(&error);
conn = dbus_bus_get(DBUS_BUS_SESSION, &error);
if (dbus_error_is_set(&error)) {
fprintf(stderr, "Error connecting to the D-Bus session bus: %s\n", error.message);
dbus_error_free(&error);
return 1;
}
DBusMessage *message = dbus_message_new_method_call("org.freedesktop.Notifications",
"/org/freedesktop/Notifications",
"org.freedesktop.Notifications",
"Notify");
DBusMessageIter args, array;
dbus_message_iter_init_append(message, &args);
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &app_name);
dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, ¬ification_id);
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &app_icon);
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &summary);
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &body);
dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &array);
dbus_message_iter_close_container(&args, &array);
DBusMessageIter dict_entry;
const char *key = "";
dbus_uint32_t value = 0;
dbus_message_iter_open_container(&args, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry);
dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING, &key);
DBusMessageIter variant;
dbus_message_iter_open_container(&dict_entry, DBUS_TYPE_VARIANT, DBUS_TYPE_UINT32_AS_STRING, &variant);
dbus_message_iter_append_basic(&variant, DBUS_TYPE_UINT32, &value);
dbus_message_iter_close_container(&dict_entry, &variant);
dbus_message_iter_close_container(&args, &dict_entry);
dbus_message_iter_append_basic(&args, DBUS_TYPE_INT32, &timeout);
DBusPendingCall* pending;
dbus_connection_send_with_reply(conn, message, &pending, DBUS_TIMEOUT_INFINITE);
dbus_connection_flush(conn);
dbus_message_unref(message);
dbus_pending_call_block(pending);
message = dbus_pending_call_steal_reply(pending);
dbus_pending_call_unref(pending);
DBusMessageIter args2;
int arg, id;
if(!dbus_message_iter_init(message, &args2))
fprintf(stderr, "message has no arugments!\n");
else if (DBUS_TYPE_UINT32 != (arg=dbus_message_iter_get_arg_type(&args2))) {
fprintf(stderr, "Argument is not int!\n");
switch(arg){
case DBUS_TYPE_STRING:
char *string;
dbus_message_iter_get_basic(&args2, &string);
fprintf(stderr, "ITS A STRING: %s\n", string);
break;
}}
else {
dbus_message_iter_get_basic(&args2, &id);
printf("ID: %d\n", id);
}
dbus_message_unref(message);
dbus_connection_close(conn);
return 0;
}
Edit: I fixed the segfaults, but now I get a even more obsucure reply from dbus when reading the reply arguments
While this doesn’t directly answer your question, it will help in the long run: You probably don’t want to use dbus-glib or libdbus to connect to D-Bus. Both are old and have poor API designs which make them hard to use. Using a D-Bus binding with a better-designed API will make your code easier to write and maintain in the long run.
See this answer for a comparison of the D-Bus bindings available to you which are easier to use. It is unfortunately quite confusing that there are so many bindings and that some of them are not recommended.
Related
I have a little problem about paho mqtt library.
I register a callback function MQTTClient_messageArrived and MQTTClient_connectionLost.
And I call MQTTClient_subscribe() or MQTTClient_unsubscribe() in this callback function. After running this callback function. I got a error code (-3) from MQTTClient_connectionLost. Furthermore, I print the cause is NULL.
Is any possible problem to my problem? Thanks
Here are my initialize function and callback function:
void mqtt_initialize(void){
int rc;
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
if(MQTTClient_create(&g_MQTT_client, BROKER_ADDR, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL) < 0){
g_mqtt_exist = MQTT_INSTANCE_NOT_EXIST;
} else {
g_mqtt_exist = MQTT_INSTANCE_EXIST;
}
conn_opts.keepAliveInterval = 20;
conn_opts.cleansession = 1;
MQTTClient_setCallbacks(g_MQTT_client, NULL, mqtt_connlost_cb, mqtt_arrived_cb, mqtt_delivered_cb);
if ((rc = MQTTClient_connect(g_MQTT_client, &conn_opts)) != MQTTCLIENT_SUCCESS) {
MAIN_PRINT_DEBUG(0, "Failed to connect MQTT broker, return code %d\n", rc);
mqtt_free();
}
/* set subscribe control topic */
MQTTClient_subscribe(g_MQTT_client, "topic_1", MQTT_QOS);
}
int mqtt_arrived_cb(void *context, char *topicName, int topicLen, MQTTClient_message *message)
{
int ret = 0;
printf("topic: %s\n", topicName);
if(!strcmp(topicName, "topic_1")){
printf("topic_1\n");
ret = MQTTClient_subscribe(g_MQTT_client, "topic_2", 0);
if (ret < 0){
printf("mqtt operation fail\n");
}
}
return 1;
}
void mqtt_connlost_cb(void *context, char *cause)
{
MAIN_PRINT_DEBUG(0, "\n MQTT Connection lost\n");
MAIN_PRINT_DEBUG(0," cause: %s\n", cause);
}
Did you try connecting any other client to same broker with same client id? That could be a possible case if you are getting connection lost. Also if your callback throws exception, it will cause connection lost, however in this case paho will reconnect.
I am implimenting the StatusNotifierWatcher service using the low-level DBus C library. The StatusNotifierWatcher specification requires that the watcher can know when "A StatusNotifierItem instance has disappeared from the bus" So that it can send the StatusNotifierItemUnregistered signal.
An example implimentation:
#include <stdio.h>
#include <stdlib.h>
#include <dbus/dbus.h>
DBusConnection *conn = NULL;
void item_unregistered_signal(const char *name) {
DBusMessage *signal = dbus_message_new_signal(
"/org/freedesktop/StatusNotifierWatcher",
"org.freedesktop.StatusNotifierWatcher",
"StatusNotifierItemUnregistered");
dbus_message_append_args(signal,
DBUS_TYPE_STRING, &name,
DBUS_TYPE_INVALID);
dbus_connection_send(conn, signal, NULL);
dbus_message_unref(signal);
}
void watch_name(const char *name, void(*cb)(const char *)) {
// Not sure how to impliment
}
dbus_bool_t register_item(DBusConnection *connection, DBusMessage *message, void *_data) {
DBusError error;
char *name;
if (!dbus_message_get_args(message, &error,
DBUS_TYPE_STRING, &name,
DBUS_TYPE_INVALID)) {
fprintf(stderr, "Error parsing method args: %s\n", error.message);
return FALSE;
}
watch_name(name, item_unregistered_signal);
return TRUE;
}
static void check_and_abort(DBusError *error) {
if (dbus_error_is_set(error)) {
fprintf(stderr, "dbus_err: %s\n", error->message);
exit(EXIT_FAILURE);
}
}
int main() {
DBusError error;
dbus_error_init(&error);
conn = dbus_bus_get(DBUS_BUS_SESSION, &error);
check_and_abort(&error);
dbus_bus_request_name(conn, "org.freedesktop.StatusNotifierWatcher",
DBUS_NAME_FLAG_REPLACE_EXISTING,
&error);
check_and_abort(&error);
dbus_connection_add_filter(conn, register_item, NULL, free);
while(1) {
dbus_connection_read_write_dispatch(conn, 1000);
}
}
If I have the well-known name to a DBus service, how do I know when the name disappears from the bus?
Well, I figured this out and I'll post an answer for any future poor souls who need to work with libdbus.
org.freedesktop.DBus sends the NameOwnerChanged signal whenever any DBus name changes. One can use this signal to track if an item has disappeared because the NewOwner argument is a null string.
This function can do that:
static DBusHandlerResult signal_handler(DBusConnection *connection,
DBusMessage *message, void *_usr_data) {
if (dbus_message_is_signal(message, "org.freedesktop.DBus",
"NameOwnerChanged")) {
const char *name;
const char *old_owner;
const char *new_owner;
if (!dbus_message_get_args(message, NULL,
DBUS_TYPE_STRING, &name,
DBUS_TYPE_STRING, &old_owner,
DBUS_TYPE_STRING, &new_owner,
DBUS_TYPE_INVALID)) {
fprintf(stderr, "Error getting OwnerChanged args");
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
if (strcmp(name, "") != 0) {
// Name not lost, just swapped owners
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
name_lost(name);
return DBUS_HANDLER_RESULT_HANDLED;
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
One also needs to add a match so that your program will be called with this signal. I added this in main()
dbus_bus_add_match(conn,
"type='signal',\
sender='org.freedesktop.DBus',\
interface='org.freedesktop.DBus',\
member='NameOwnerChanged'",
&error);
check_and_abort(&error);
dbus_connection_add_filter(conn, signal_handler, NULL, free);
For those using GDBus (recommended) instead of libdbus (discouraged), the equivalent is g_bus_watch_name().
My goal: Is to monitor the state of my network interface (mainly wireless) from my firmware (in C) by monitoring the wpa_supplicant through the D-Bus interfaces. I would like to stick with C and low-level API of D-bus.
What I have so far
I've written a small program in C, copied most of the code as is from this SO user.
I've gone through all possible tutorials on D-Bus and wpa_supplicant
My program compiles and works properly. However it does not produce the expected output.
Here's my code:
#include <stdio.h>
#include <dbus/dbus.h>
#define WPAS_DBUS_SERVICE "fi.epitest.hostap.WPASupplicant"
#define WPAS_DBUS_PATH "/fi/epitest/hostap/WPASupplicant"
#define WPAS_DBUS_INTERFACE "fi.epitest.hostap.WPASupplicantAAA"
#define WPAS_DBUS_PATH_INTERFACES WPAS_DBUS_PATH "/Interfaces"
#define WPAS_DBUS_IFACE_INTERFACE WPAS_DBUS_INTERFACE ".Interfaces"
#define WPAS_DBUS_NETWORKS_PART "Networks"
#define WPAS_DBUS_IFACE_NETWORK WPAS_DBUS_INTERFACE ".Network"
#define WPAS_DBUS_BSSIDS_PART "BSSIDs"
#define WPAS_DBUS_IFACE_BSSID WPAS_DBUS_INTERFACE ".BSSID"
int ret;
char signalDesc[1024]; // Signal description as string
// Signal handling
signal(SIGKILL, stopLoop);
signal(SIGTERM, stopLoop);
void loop(DBusConnection* conn)
{
DBusMessage* msg;
DBusMessageIter args;
DBusMessageIter subArgs;
int argType;
int i;
int buffSize = 1024;
char strValue[buffSize];
const char* member = 0;
while (1)
{
// non blocking read of the next available message
dbus_connection_read_write(conn, 0);
msg = dbus_connection_pop_message(conn);
// loop again if we haven't read a message
if (!msg)
{
printf("No message received, waiting a little ...\n");
sleep(1);
continue;
}
else printf("Got a message, will analyze it ...\n");
// Print the message member
printf("Got message for interface %s\n",
dbus_message_get_interface(msg));
member = dbus_message_get_member(msg);
if(member) printf("Got message member %s\n", member);
// Check has argument
if (!dbus_message_iter_init(msg, &args))
{
printf("Message has no argument\n");
continue;
}
else
{
// Go through arguments
while(1)
{
argType = dbus_message_iter_get_arg_type(&args);
if (argType == DBUS_TYPE_STRING)
{
printf("Got string argument, extracting ...\n");
char* str = NULL;
dbus_message_iter_get_basic(&args, &str);
printf("Received string: \n %s \n",str);
}
else
printf("Arg type not implemented yet !\n");
if(dbus_message_iter_has_next(&args))
dbus_message_iter_next(&args);
else break;
}
printf("No more arguments!\n");
}
// free the message
dbus_message_unref(msg);
}
}
int main()
{
DBusConnection *connection;
DBusError error;
char *name = "org.share.linux";
dbus_error_init(&error);
connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
if ( dbus_error_is_set(&error) )
{
printf("Error connecting to the daemon bus: %s",error.message);
dbus_error_free(&error);
return 1;
}
// request a name on the bus
ret = dbus_bus_request_name(connection, WPAS_DBUS_SERVICE, 0, &error);
if (dbus_error_is_set(&error))
{
printf(stderr, "Name Error (%s)\n", error.message);
dbus_error_free(&error);
}
/* Connect to signal */
// Interface signal ..
printf(signalDesc, "type='signal',interface='%s'",WPAS_DBUS_IFACE_INTERFACE);
dbus_bus_add_match(connection, signalDesc, &error);
dbus_connection_flush(connection);
if (dbus_error_is_set(&error))
{
fprintf(stderr, "Match Error (%s)\n", error.message);
return 1;
}
// Do main loop
loop(connection);
dbus_connection_close(connection);
return 0;
}
List of D-bus services on my BBB
Output
Some pointers
I would like to catch the signals as shown in the D-Bus API of wpa_supplicant.
Some things I would like to do -- see when a wireless interface say wlan0 is enabled, connects to access point etc. Also capability to set AP and stuff.
Its catching signal from other interfaces for which no match has been added.
I run this program and change the state of the networking interfaces but I dont get any signals. Also, I dont know if requesting name on the bus is necessary as I'm just listening.
What's the possible issue here? Any pointers will be really helpful.
I saw this question but it doesn't seem to apply.
Heres my function which does most the heavy lifting in the client code:
void
database_1(char *host, char *action, char *message)
{
printf("Action: %s\n", action);
printf("Message: %s\n", message);
CLIENT *clnt;
rpc_args *result_1;
//struct rpc_args action_1_arg;
//rpc arguments struct to pass to server
struct rpc_args *args = malloc(sizeof(struct rpc_args));
char *id = generate_id();
if (strcmp(action, "GET") == 0) {
printf("Client: GET request\n");
strcpy(args->action, action);
strcpy(args->id, id);
} else if(strcmp(action, "PUT") == 0) {
printf("Client: PUT request\n");
strcpy(args->action, action);
strcpy(args->id, id);
strcpy(args->message.content, message);
}
#ifndef DEBUG
clnt = clnt_create (host, DATABASE, ASSIGNMENT_7, "udp");
if (clnt == NULL) {
clnt_pcreateerror (host);
exit (1);
}
#endif /* DEBUG */
result_1 = action_1(args, clnt);
if (result_1 == (rpc_args *) NULL) {
clnt_perror (clnt, "call failed");
}
free(args);
#ifndef DEBUG
clnt_destroy (clnt);
#endif /* DEBUG */
}
Here's my output:
./database_client eecslinab3.case.edu GET
running client, main
Action: GET
Message: (null)
hostname is eecslinab3
The process id is 24697
The unique id is eecslinab324697
Client: GET request
call failed: RPC: Can't encode arguments
Database.x
struct message {
char content[2000];
};
struct rpc_args {
char action[20];
char id[1024];
struct message message;
};
program DATABASE {
version ASSIGNMENT_7 {
rpc_args ACTION(struct rpc_args) = 1;
} = 1;
} = 0x20fff100;
A friend helped me solve the problem but didn't give me much details about why or how the fix works. Basically I reduced the char array sizes in my struct and it worked. Has something to do with the limit of data you can send over UDP.
struct rpc_args {
char action[20];
char id[80];
char message[80];
};
program DATABASE {
version ASSIGNMENT_7 {
rpc_args ACTION(struct rpc_args) = 1;
} = 1;
} = 0x20fff100;
The problem I am having is specifically printing out the response of a dbus method call in C using the low level API. I am new to C's libdbus, but have done some work in python-dbus.
I know how to write dbus methods and method calls in python as well as the CLI
I can find code on the internet to invoke dbus methods, but they don't return or print out the response
I have been looking at the libdbus doxygen api, but cannot determine how to pull out the response.
The way I have my code set up, a python dbus daemon runs with methods I want to call. Some of them return a string. I want a C program to connect to the session bus, call the method, print out the reply and exit.
This is what I have currently:
#include <stdio.h>
#include <dbus/dbus.h>
static void send_dbus_message (DBusConnection *connection, const char *msg)
{
DBusMessage *message;
//initialize the message
message = dbus_message_new_signal ("/org/example/foo/bar",
"org.example.foo.bar",
msg);
//send the message
dbus_connection_send (connection, message, NULL);
//deallocate the message
dbus_message_unref (message);
}
int main (int argc, char **argv)
{
DBusConnection *connection;
DBusError error;
//init error message
dbus_error_init (&error);
connection = dbus_bus_get (DBUS_BUS_SESSION, &error);
if (!connection)
{
printf ("Connection to D-BUS daemon failed: %s", error.message);
//deallocate error message
dbus_error_free (&error);
return 1;
}
send_dbus_message (connection, "HelloWorld");
return 0;
}
Can be synchronous or asynchronous.
You can use the method mentioned in http://www.matthew.ath.cx/misc/dbus to get a method reply message.
Once you have a dbus message you can use following method to extract the data.
To parse a dbus message, you need a argument iterator. Initalize it to read contents of the incoming message.
DBusMessageIter MsgIter;
dbus_message_iter_init(msg, &MsgIter);//msg is pointer to dbus message received
You have to validate the signature of the incoming message before reading it. Or you can also go for argument by argument verification. For example, if the argument type is string
if (DBUS_TYPE_STRING == dbus_message_iter_get_arg_type(&MsgIter)){
char* str = NULL;
dbus_message_iter_get_basic(&MsgIter, &str);//this function is used to read basic dbus types like int, string etc.
}
For complex types, like structures, arrays, variants and dict entries, you have to create corresponding child iterators to parse contents of each complex element. Say, for a dbus signature ofs(i{ii}i)u, the extraction is done as below
//Parsing a signature s(i{ii}i)u
DBusMessageIter rootIter;
dbus_message_iter_init(msg, &rootIter);
if (DBUS_TYPE_STRING == dbus_message_iter_get_arg_type(&rootIter))
{
char* str = NULL;
dbus_message_iter_get_basic(&rootIter, &str);//this function is used to read basic dbus types like int, string etc.
dbus_message_iter_next(&rootIter);//Go to next argument of root iter
//Block to enter and read structure
if (DBUS_TYPE_STRUCT == dbus_message_iter_get_arg_type(&rootIter))
{
DBusMessageIter structIter;
dbus_message_iter_recurse(&rootIter, &structIter);//Initialize iterator for struct
//Argument 1 is int32
if (DBUS_TYPE_INT32 == dbus_message_iter_get_arg_type(&structIter))
{
int a;
dbus_message_iter_get_basic(&structIter, &a);//Read integer
dbus_message_iter_next(&structIter);//Go to next argument of structiter
if (DDBUS_TYPE_DICT_ENTRY == dbus_message_iter_get_arg_type(&structIter))
{
DBusMessageIter dictIter;
dbus_message_iter_recurse(&structIter, &dictIter);//Initialize iterator for dictentry
if (DBUS_TYPE_INT32 == dbus_message_iter_get_arg_type(&dictIter))
{
dbus_message_iter_get_basic(&dictIter, &a);//Read integer
dbus_message_iter_next(&dictIter);//Go to next argument of dictentry
if (DBUS_TYPE_INT32 == dbus_message_iter_get_arg_type(&dictIter))
{
dbus_message_iter_get_basic(&dictIter, &a);//Read integer
}
}
}
dbus_message_iter_next(&structIter);//Go to next argument of structiter
if (DBUS_TYPE_INT32 == dbus_message_iter_get_arg_type(&structIter))
{
dbus_message_iter_get_basic(&structIter, &a);//Read integer
}
}
}
dbus_message_iter_next(&rootIter);//Go to next argument of root iterator
if (DBUS_TYPE_UINT32 == dbus_message_iter_get_arg_type(&rootIter))
{
uint32_t b;
dbus_message_iter_get_basic(&rootIter, &b);//Read integer
}
}
In above code, I used argument by argument signature check. Instead you can do a one time verfication using dbus_message_iter_get_signature. Refer to libdbus api for more info.
#
From your reply I understand that you have problems with connection setup ,
here is a full example, where a method call is invoked on a server and the result is printed
if the first argument is a string.
#
#include <stdio.h>
#include <stdlib.h>
#include <dbus/dbus.h>
#include <assert.h>
DBusConnection* conn = NULL;
//Helper function to setup connection
void vsetupconnection();
//Send method call, Returns NULL on failure, else pointer to reply
DBusMessage* sendMethodCall(const char* objectpath, \
const char* busname, \
const char* interfacename, \
const char* methodname);
#define TEST_BUS_NAME "org.freedesktop.DBus"
#define TEST_OBJ_PATH "/org/freedesktop/DBus"
#define TEST_INTERFACE_NAME "org.freedesktop.DBus.Introspectable"
#define TEST_METHOD_NAME "Introspect"
int main (int argc, char **argv)
{
vsetupconnection();
DBusMessage* reply = sendMethodCall(TEST_OBJ_PATH, TEST_BUS_NAME, TEST_INTERFACE_NAME, TEST_METHOD_NAME);
if(reply != NULL) {
DBusMessageIter MsgIter;
dbus_message_iter_init(reply, &MsgIter);//msg is pointer to dbus message received
if (DBUS_TYPE_STRING == dbus_message_iter_get_arg_type(&MsgIter)){
char* str = NULL;
dbus_message_iter_get_basic(&MsgIter, &str);
printf("Received string: \n %s \n",str);
}
dbus_message_unref(reply);//unref reply
}
dbus_connection_close(conn);
return 0;
}
void vsetupconnection()
{
DBusError err;
// initialise the errors
dbus_error_init(&err);
// connect to session bus
conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
if (dbus_error_is_set(&err)) {
printf("Connection Error (%s)\n", err.message);
dbus_error_free(&err);
}
if (NULL == conn) {
exit(1);
}
else {
printf("Connected to session bus\n");
}
}
DBusMessage* sendMethodCall(const char* objectpath, const char* busname, const char* interfacename, const char* methodname)
{
assert(objectpath != NULL); assert(busname != NULL); assert(interfacename != NULL);
assert(methodname != NULL); assert(conn != NULL);
DBusMessage* methodcall = dbus_message_new_method_call(busname,objectpath, interfacename, methodname);
if (methodcall == NULL) {
printf("Cannot allocate DBus message!\n");
}
//Now do a sync call
DBusPendingCall* pending;
DBusMessage* reply;
if (!dbus_connection_send_with_reply(conn, methodcall, &pending, -1))//Send and expect reply using pending call object
{
printf("failed to send message!\n");
}
dbus_connection_flush(conn);
dbus_message_unref(methodcall);
methodcall = NULL;
dbus_pending_call_block(pending);//Now block on the pending call
reply = dbus_pending_call_steal_reply(pending);//Get the reply message from the queue
dbus_pending_call_unref(pending);//Free pending call handle
assert(reply != NULL);
if(dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
printf("Error : %s",dbus_message_get_error_name(reply));
dbus_message_unref(reply);
reply = NULL;
}
return reply;
}