-
Notifications
You must be signed in to change notification settings - Fork 31
/
Copy pathdbus-server.c
325 lines (268 loc) · 9.45 KB
/
dbus-server.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib-lowlevel.h> /* for glib main loop */
const char *version = "0.1";
GMainLoop *mainloop;
/*
* This is the XML string describing the interfaces, methods and
* signals implemented by our 'Server' object. It's used by the
* 'Introspect' method of 'org.freedesktop.DBus.Introspectable'
* interface.
*
* Currently our tiny server implements only 3 interfaces:
*
* - org.freedesktop.DBus.Introspectable
* - org.freedesktop.DBus.Properties
* - org.example.TestInterface
*
* 'org.example.TestInterface' offers 3 methods:
*
* - Ping(): makes the server answering the string 'Pong'.
* It takes no arguments.
*
* - Echo(): replies the passed string argument.
*
* - EmitSignal(): send a signal 'OnEmitSignal'
*
* - Quit(): makes the server exit. It takes no arguments.
*/
const char *server_introspection_xml =
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
"<node>\n"
" <interface name='org.freedesktop.DBus.Introspectable'>\n"
" <method name='Introspect'>\n"
" <arg name='data' type='s' direction='out' />\n"
" </method>\n"
" </interface>\n"
" <interface name='org.freedesktop.DBus.Properties'>\n"
" <method name='Get'>\n"
" <arg name='interface' type='s' direction='in' />\n"
" <arg name='property' type='s' direction='in' />\n"
" <arg name='value' type='s' direction='out' />\n"
" </method>\n"
" <method name='GetAll'>\n"
" <arg name='interface' type='s' direction='in'/>\n"
" <arg name='properties' type='a{sv}' direction='out'/>\n"
" </method>\n"
" </interface>\n"
" <interface name='org.example.TestInterface'>\n"
" <property name='Version' type='s' access='read' />\n"
" <method name='Ping' >\n"
" <arg type='s' direction='out' />\n"
" </method>\n"
" <method name='Echo'>\n"
" <arg name='string' direction='in' type='s'/>\n"
" <arg type='s' direction='out' />\n"
" </method>\n"
" <method name='EmitSignal'>\n"
" </method>\n"
" <method name='Quit'>\n"
" </method>\n"
" <signal name='OnEmitSignal'>\n"
" </signal>"
" </interface>\n"
"</node>\n";
/*
* This implements 'Get' method of DBUS_INTERFACE_PROPERTIES so a
* client can inspect the properties/attributes of 'TestInterface'.
*/
DBusHandlerResult server_get_properties_handler(const char *property, DBusConnection *conn, DBusMessage *reply)
{
if (!strcmp(property, "Version")) {
dbus_message_append_args(reply,
DBUS_TYPE_STRING, &version,
DBUS_TYPE_INVALID);
} else
/* Unknown property */
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
if (!dbus_connection_send(conn, reply, NULL))
return DBUS_HANDLER_RESULT_NEED_MEMORY;
return DBUS_HANDLER_RESULT_HANDLED;
}
/*
* This implements 'GetAll' method of DBUS_INTERFACE_PROPERTIES. This
* one seems required by g_dbus_proxy_get_cached_property().
*/
DBusHandlerResult server_get_all_properties_handler(DBusConnection *conn, DBusMessage *reply)
{
DBusHandlerResult result;
DBusMessageIter array, dict, iter, variant;
const char *property = "Version";
/*
* All dbus functions used below might fail due to out of
* memory error. If one of them fails, we assume that all
* following functions will fail too, including
* dbus_connection_send().
*/
result = DBUS_HANDLER_RESULT_NEED_MEMORY;
dbus_message_iter_init_append(reply, &iter);
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &array);
/* Append all properties name/value pairs */
property = "Version";
dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &dict);
dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &property);
dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, "s", &variant);
dbus_message_iter_append_basic(&variant, DBUS_TYPE_STRING, &version);
dbus_message_iter_close_container(&dict, &variant);
dbus_message_iter_close_container(&array, &dict);
dbus_message_iter_close_container(&iter, &array);
if (dbus_connection_send(conn, reply, NULL))
result = DBUS_HANDLER_RESULT_HANDLED;
return result;
}
/*
* This function implements the 'TestInterface' interface for the
* 'Server' DBus object.
*
* It also implements 'Introspect' method of
* 'org.freedesktop.DBus.Introspectable' interface which returns the
* XML string describing the interfaces, methods, and signals
* implemented by 'Server' object. This also can be used by tools such
* as d-feet(1) and can be queried by:
*
* $ gdbus introspect --session --dest org.example.TestServer --object-path /org/example/TestObject
*/
DBusHandlerResult server_message_handler(DBusConnection *conn, DBusMessage *message, void *data)
{
DBusHandlerResult result;
DBusMessage *reply = NULL;
DBusError err;
bool quit = false;
fprintf(stderr, "Got D-Bus request: %s.%s on %s\n",
dbus_message_get_interface(message),
dbus_message_get_member(message),
dbus_message_get_path(message));
/*
* Does not allocate any memory; the error only needs to be
* freed if it is set at some point.
*/
dbus_error_init(&err);
if (dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE, "Introspect")) {
if (!(reply = dbus_message_new_method_return(message)))
goto fail;
dbus_message_append_args(reply,
DBUS_TYPE_STRING, &server_introspection_xml,
DBUS_TYPE_INVALID);
} else if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "Get")) {
const char *interface, *property;
if (!dbus_message_get_args(message, &err,
DBUS_TYPE_STRING, &interface,
DBUS_TYPE_STRING, &property,
DBUS_TYPE_INVALID))
goto fail;
if (!(reply = dbus_message_new_method_return(message)))
goto fail;
result = server_get_properties_handler(property, conn, reply);
dbus_message_unref(reply);
return result;
} else if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "GetAll")) {
if (!(reply = dbus_message_new_method_return(message)))
goto fail;
result = server_get_all_properties_handler(conn, reply);
dbus_message_unref(reply);
return result;
} else if (dbus_message_is_method_call(message, "org.example.TestInterface", "Ping")) {
const char *pong = "Pong";
if (!(reply = dbus_message_new_method_return(message)))
goto fail;
dbus_message_append_args(reply,
DBUS_TYPE_STRING, &pong,
DBUS_TYPE_INVALID);
} else if (dbus_message_is_method_call(message, "org.example.TestInterface", "Echo")) {
const char *msg;
if (!dbus_message_get_args(message, &err,
DBUS_TYPE_STRING, &msg,
DBUS_TYPE_INVALID))
goto fail;
if (!(reply = dbus_message_new_method_return(message)))
goto fail;
dbus_message_append_args(reply,
DBUS_TYPE_STRING, &msg,
DBUS_TYPE_INVALID);
} else if (dbus_message_is_method_call(message, "org.example.TestInterface", "EmitSignal")) {
if (!(reply = dbus_message_new_signal("/org/example/TestObject",
"org.example.TestInterface",
"OnEmitSignal")))
goto fail;
if (!dbus_connection_send(conn, reply, NULL))
return DBUS_HANDLER_RESULT_NEED_MEMORY;
/* Send a METHOD_RETURN reply. */
reply = dbus_message_new_method_return(message);
} else if (dbus_message_is_method_call(message, "org.example.TestInterface", "Quit")) {
/*
* Quit() has no return values but a METHOD_RETURN
* reply is required, so the caller will know the
* method was successfully processed.
*/
reply = dbus_message_new_method_return(message);
quit = true;
} else
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
fail:
if (dbus_error_is_set(&err)) {
if (reply)
dbus_message_unref(reply);
reply = dbus_message_new_error(message, err.name, err.message);
dbus_error_free(&err);
}
/*
* In any cases we should have allocated a reply otherwise it
* means that we failed to allocate one.
*/
if (!reply)
return DBUS_HANDLER_RESULT_NEED_MEMORY;
/* Send the reply which might be an error one too. */
result = DBUS_HANDLER_RESULT_HANDLED;
if (!dbus_connection_send(conn, reply, NULL))
result = DBUS_HANDLER_RESULT_NEED_MEMORY;
dbus_message_unref(reply);
if (quit) {
fprintf(stderr, "Server exiting...\n");
g_main_loop_quit(mainloop);
}
return result;
}
const DBusObjectPathVTable server_vtable = {
.message_function = server_message_handler
};
int main(void)
{
DBusConnection *conn;
DBusError err;
int rv;
dbus_error_init(&err);
/* connect to the daemon bus */
conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
if (!conn) {
fprintf(stderr, "Failed to get a session DBus connection: %s\n", err.message);
goto fail;
}
rv = dbus_bus_request_name(conn, "org.example.TestServer", DBUS_NAME_FLAG_REPLACE_EXISTING , &err);
if (rv != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
fprintf(stderr, "Failed to request name on bus: %s\n", err.message);
goto fail;
}
if (!dbus_connection_register_object_path(conn, "/org/example/TestObject", &server_vtable, NULL)) {
fprintf(stderr, "Failed to register a object path for 'TestObject'\n");
goto fail;
}
/*
* For the sake of simplicity we're using glib event loop to
* handle DBus messages. This is the only place where glib is
* used.
*/
printf("Starting dbus tiny server v%s\n", version);
mainloop = g_main_loop_new(NULL, false);
/* Set up the DBus connection to work in a GLib event loop */
dbus_connection_setup_with_g_main(conn, NULL);
/* Start the glib event loop */
g_main_loop_run(mainloop);
return EXIT_SUCCESS;
fail:
dbus_error_free(&err);
return EXIT_FAILURE;
}