reverted changeset [3215], as we need NULL callback to do asynchronous calls
[strongswan.git] / src / charon / control / interfaces / dbus_interface.c
1 /**
2 * @file dbus_interface.c
3 *
4 * @brief Implementation of dbus_interface_t.
5 *
6 */
7
8 /*
9 * Copyright (C) 2007 Martin Willi
10 * Hochschule fuer Technik Rapperswil
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
21 */
22
23 #define DBUS_API_SUBJECT_TO_CHANGE
24 #include <dbus/dbus.h>
25 #include <NetworkManager/NetworkManager.h>
26 #include <NetworkManager/NetworkManagerVPN.h>
27 #include <stdlib.h>
28
29 #include "dbus_interface.h"
30
31 #include <library.h>
32 #include <daemon.h>
33 #include <processing/jobs/callback_job.h>
34
35
36 #define NM_DBUS_SERVICE_STRONG "org.freedesktop.NetworkManager.strongswan"
37 #define NM_DBUS_INTERFACE_STRONG "org.freedesktop.NetworkManager.strongswan"
38 #define NM_DBUS_PATH_STRONG "/org/freedesktop/NetworkManager/strongswan"
39
40 typedef struct private_dbus_interface_t private_dbus_interface_t;
41
42 /**
43 * Private data of an dbus_interface_t object.
44 */
45 struct private_dbus_interface_t {
46
47 /**
48 * Public part of dbus_t object.
49 */
50 dbus_interface_t public;
51
52 /**
53 * DBUS connection
54 */
55 DBusConnection* conn;
56
57 /**
58 * error value used here and there
59 */
60 DBusError err;
61
62 /**
63 * state of the daemon
64 */
65 NMVPNState state;
66
67 /**
68 * job accepting stroke messages
69 */
70 callback_job_t *job;
71
72 /**
73 * name of the currently active connection
74 */
75 char *name;
76 };
77
78 /**
79 * set daemon state and send StateChange signal to the bus
80 */
81 static void set_state(private_dbus_interface_t *this, NMVPNState state)
82 {
83 DBusMessage* msg;
84
85 msg = dbus_message_new_signal(NM_DBUS_PATH_STRONG, NM_DBUS_INTERFACE_STRONG, NM_DBUS_VPN_SIGNAL_STATE_CHANGE);
86
87 if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, &this->state,
88 DBUS_TYPE_UINT32, &state, DBUS_TYPE_INVALID) ||
89 !dbus_connection_send(this->conn, msg, NULL))
90 {
91 DBG1(DBG_CFG, "unable to send DBUS StateChange signal");
92 }
93 dbus_connection_flush(this->conn);
94 dbus_message_unref(msg);
95 this->state = state;
96 }
97
98
99 /**
100 * get the child_cfg with the same name as the peer cfg
101 */
102 static child_cfg_t* get_child_from_peer(peer_cfg_t *peer_cfg, char *name)
103 {
104 child_cfg_t *current, *found = NULL;
105 iterator_t *iterator;
106
107 iterator = peer_cfg->create_child_cfg_iterator(peer_cfg);
108 while (iterator->iterate(iterator, (void**)&current))
109 {
110 if (streq(current->get_name(current), name))
111 {
112 found = current;
113 found->get_ref(found);
114 break;
115 }
116 }
117 iterator->destroy(iterator);
118 return found;
119 }
120
121
122 /**
123 * process NetworkManagers startConnection method call
124 */
125 static bool start_connection(private_dbus_interface_t *this, DBusMessage* msg)
126 {
127 DBusMessage *reply, *signal;
128 char *name, *user, **data, **passwords, **routes;
129 int data_count, passwords_count, routes_count;
130 u_int32_t me, other, p2p, netmask, mss;
131 char *dev, *domain, *banner;
132 const dbus_int32_t array[] = {};
133 const dbus_int32_t *varray = array;
134 peer_cfg_t *peer_cfg;
135 child_cfg_t *child_cfg;
136 status_t status = FAILED;
137
138 dbus_error_free(&this->err);
139
140 if (!dbus_message_get_args(msg, &this->err,
141 DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &user,
142 DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &passwords, &passwords_count,
143 DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &data, &data_count,
144 DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &routes, &routes_count,
145 DBUS_TYPE_INVALID))
146 {
147 return FALSE;
148 }
149 set_state(this, NM_VPN_STATE_STARTING);
150
151 peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends, name);
152 if (peer_cfg)
153 {
154 free(this->name);
155 this->name = strdup(peer_cfg->get_name(peer_cfg));
156 child_cfg = get_child_from_peer(peer_cfg, name);
157 if (child_cfg)
158 {
159 status = charon->interfaces->initiate(charon->interfaces,
160 peer_cfg, child_cfg, interface_manager_cb_empty, NULL);
161 }
162 else
163 {
164 peer_cfg->destroy(peer_cfg);
165 }
166 }
167 reply = dbus_message_new_method_return(msg);
168 dbus_connection_send(this->conn, reply, NULL);
169 dbus_message_unref(reply);
170
171 if (status == SUCCESS)
172 {
173
174 set_state(this, NM_VPN_STATE_STARTED);
175 signal = dbus_message_new_signal(NM_DBUS_PATH_STRONG,
176 NM_DBUS_INTERFACE_STRONG,
177 NM_DBUS_VPN_SIGNAL_IP4_CONFIG);
178 me = other = p2p = mss = netmask = 0;
179 dev = domain = banner = "";
180 if (dbus_message_append_args(signal,
181 DBUS_TYPE_UINT32, &other,
182 DBUS_TYPE_STRING, &dev,
183 DBUS_TYPE_UINT32, &me,
184 DBUS_TYPE_UINT32, &p2p,
185 DBUS_TYPE_UINT32, &netmask,
186 DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &varray, 0,
187 DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &varray, 0,
188 DBUS_TYPE_UINT32, &mss,
189 DBUS_TYPE_STRING, &domain,
190 DBUS_TYPE_STRING, &banner, DBUS_TYPE_INVALID))
191 {
192 dbus_connection_send(this->conn, signal, NULL);
193 }
194 dbus_message_unref(signal);
195 }
196 else
197 {
198 set_state(this, NM_VPN_STATE_STOPPED);
199 }
200
201 dbus_connection_flush(this->conn);
202 return TRUE;
203 }
204
205 /**
206 * process NetworkManagers stopConnection method call
207 */
208 static bool stop_connection(private_dbus_interface_t *this, DBusMessage* msg)
209 {
210 u_int32_t id;
211 iterator_t *iterator;
212 ike_sa_t *ike_sa;
213
214 if (this->name == NULL)
215 {
216 return FALSE;
217 }
218
219 dbus_error_free(&this->err);
220
221 set_state(this, NM_VPN_STATE_STOPPING);
222
223 iterator = charon->interfaces->create_ike_sa_iterator(charon->interfaces);
224 while (iterator->iterate(iterator, (void**)&ike_sa))
225 {
226 child_sa_t *child_sa;
227 iterator_t *children;
228
229 if (this->name && streq(this->name, ike_sa->get_name(ike_sa)))
230 {
231 id = ike_sa->get_unique_id(ike_sa);
232 iterator->destroy(iterator);
233 charon->interfaces->terminate_ike(charon->interfaces, id, NULL, NULL);
234 set_state(this, NM_VPN_STATE_STOPPED);
235 return TRUE;;
236 }
237 children = ike_sa->create_child_sa_iterator(ike_sa);
238 while (children->iterate(children, (void**)&child_sa))
239 {
240 if (this->name && streq(this->name, child_sa->get_name(child_sa)))
241 {
242 id = child_sa->get_reqid(child_sa);
243 children->destroy(children);
244 iterator->destroy(iterator);
245 charon->interfaces->terminate_child(charon->interfaces, id, NULL, NULL);
246 set_state(this, NM_VPN_STATE_STOPPED);
247 return TRUE;
248 }
249 }
250 children->destroy(children);
251 }
252 iterator->destroy(iterator);
253 set_state(this, NM_VPN_STATE_STOPPED);
254 return TRUE;
255 }
256
257 /**
258 * process NetworkManagers getState method call
259 */
260 static bool get_state(private_dbus_interface_t *this, DBusMessage* msg)
261 {
262 DBusMessage* reply;
263 reply = dbus_message_new_method_return(msg);
264 if (!reply || !dbus_message_append_args(reply,
265 DBUS_TYPE_UINT32, &this->state,
266 DBUS_TYPE_INVALID))
267 {
268 return FALSE;
269 }
270 dbus_connection_send(this->conn, reply, NULL);
271 return TRUE;
272 }
273
274 /**
275 * Handle incoming messages
276 */
277 static DBusHandlerResult message_handler(DBusConnection *con, DBusMessage *msg,
278 private_dbus_interface_t *this)
279 {
280 bool handled;
281
282 if (dbus_message_is_method_call(msg, NM_DBUS_INTERFACE_STRONG,
283 "startConnection"))
284 {
285 handled = start_connection(this, msg);
286 }
287 else if (dbus_message_is_method_call(msg, NM_DBUS_INTERFACE_STRONG,
288 "stopConnection"))
289 {
290 handled = stop_connection(this, msg);
291 }
292 else if (dbus_message_is_method_call(msg, NM_DBUS_INTERFACE_STRONG,
293 "getState"))
294 {
295 handled = get_state(this, msg);
296 }
297 else
298 {
299 DBG1(DBG_CFG, "ignoring DBUS message %s.%s",
300 dbus_message_get_interface(msg), dbus_message_get_member(msg));
301 handled = FALSE;
302 }
303
304 if (handled)
305 {
306 return DBUS_HANDLER_RESULT_HANDLED;
307 }
308 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
309 }
310
311 /**
312 * Handle received signals
313
314 static DBusHandlerResult signal_handler(DBusConnection *con, DBusMessage *msg,
315 private_dbus_interface_t *this)
316 {
317 bool handled;
318
319 if (dbus_message_is_signal(msg, NM_DBUS_INTERFACE, "VPNConnectionStateChange"))
320 {
321 NMVPNState state;
322 char *name;
323
324 if (dbus_message_get_args(msg, &this->err, DBUS_TYPE_STRING, &name,
325 DBUS_TYPE_UINT32, &state, DBUS_TYPE_INVALID))
326 {
327 DBG1(DBG_CFG, "got state %d for %s", state, name);
328 }
329 handled = TRUE;
330 }
331 else
332 {
333 DBG1(DBG_CFG, "ignoring DBUS signal %s.%s",
334 dbus_message_get_interface(msg), dbus_message_get_member(msg));
335 handled = FALSE;
336 }
337 if (handled)
338 {
339 return DBUS_HANDLER_RESULT_HANDLED;
340 }
341 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
342 } */
343
344 /**
345 * dispatcher function processed by a seperate thread
346 */
347 static job_requeue_t dispatch(private_dbus_interface_t *this)
348 {
349 if (dbus_connection_read_write_dispatch(this->conn, -1))
350 {
351 return JOB_REQUEUE_DIRECT;
352 }
353 return JOB_REQUEUE_NONE;
354 }
355
356 /**
357 * Implementation of interface_t.destroy.
358 */
359 static void destroy(private_dbus_interface_t *this)
360 {
361 this->job->cancel(this->job);
362 dbus_connection_close(this->conn);
363 dbus_error_free(&this->err);
364 dbus_shutdown();
365 free(this->name);
366 free(this);
367 }
368
369 /*
370 * Described in header file
371 */
372 interface_t *interface_create()
373 {
374 int ret;
375 DBusObjectPathVTable v = {NULL, (void*)&message_handler, NULL, NULL, NULL, NULL};
376 private_dbus_interface_t *this = malloc_thing(private_dbus_interface_t);
377
378 this->public.interface.destroy = (void (*)(interface_t*))destroy;
379
380 dbus_error_init(&this->err);
381 this->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &this->err);
382 if (dbus_error_is_set(&this->err))
383 {
384 DBG1(DBG_CFG, "unable to open DBUS connection: %s", this->err.message);
385 charon->kill(charon, "DBUS initialization failed");
386 }
387 dbus_connection_set_exit_on_disconnect(this->conn, FALSE);
388
389 ret = dbus_bus_request_name(this->conn, NM_DBUS_SERVICE_STRONG,
390 DBUS_NAME_FLAG_REPLACE_EXISTING , &this->err);
391 if (dbus_error_is_set(&this->err))
392 {
393 DBG1(DBG_CFG, "unable to set DBUS name: %s", this->err.message);
394 charon->kill(charon, "unable to set DBUS name");
395 }
396 if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
397 {
398 charon->kill(charon, "DBUS name already owned");
399 }
400 if (!dbus_connection_register_object_path(this->conn, NM_DBUS_PATH_STRONG, &v, this))
401 {
402 charon->kill(charon, "unable to register DBUS message handler");
403 }
404 /*
405 if (!dbus_connection_add_filter(this->conn, (void*)signal_handler, this, NULL))
406 {
407 charon->kill(charon, "unable to register DBUS signal handler");
408 }
409
410 dbus_bus_add_match(this->conn, "type='signal', "
411 "interface='" NM_DBUS_INTERFACE_VPN "',"
412 "path='" NM_DBUS_PATH_VPN "'", &this->err);
413 if (dbus_error_is_set (&this->err))
414 {
415 charon->kill(charon, "unable to add DBUS signal match");
416 }*/
417
418 this->name = NULL;
419 this->state = NM_VPN_STATE_INIT;
420 set_state(this, NM_VPN_STATE_STOPPED);
421
422 this->job = callback_job_create((callback_job_cb_t)dispatch, this, NULL, NULL);
423 charon->processor->queue_job(charon->processor, (job_t*)this->job);
424
425 return &this->public.interface;
426 }
427