3 * Copyright 2009 Roy Marples <roy@marples.name>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 #include <dbus/dbus.h>
37 #include "libdhcpcd.h"
39 #define DHCPCD_TIMEOUT_MS 500
43 # define _unused __attribute__((__unused__))
49 DHCPCD_CONNECTION *dhcpcd_connections;
50 DHCPCD_WATCH *dhcpcd_watching;
53 dhcpcd_add_watch(DBusWatch *watch, void *data)
58 flags = dbus_watch_get_unix_fd(watch);
59 for (w = dhcpcd_watching; w; w = w->next) {
60 if (w->pollfd.fd == flags)
64 w = malloc(sizeof(*w));
67 w->next = dhcpcd_watching;
71 w->connection = (DHCPCD_CONNECTION *)data;
74 flags = dbus_watch_get_flags(watch);
75 w->pollfd.events = POLLHUP | POLLERR;
76 if (flags & DBUS_WATCH_READABLE)
77 w->pollfd.events |= POLLIN;
78 if (flags & DBUS_WATCH_WRITABLE)
79 w->pollfd.events |= POLLOUT;
80 if (w->connection->add_watch)
81 w->connection->add_watch(w->connection, &w->pollfd,
82 w->connection->watch_data);
87 dhcpcd_delete_watch(DBusWatch *watch, void *data)
92 fd = dbus_watch_get_unix_fd(watch);
94 for (w = dhcpcd_watching; w; w = w->next) {
95 if (w->pollfd.fd == fd) {
96 if (w->connection->delete_watch)
97 w->connection->delete_watch(w->connection,
98 &w->pollfd, w->connection->watch_data);
100 dhcpcd_watching = w->next;
109 static DBusHandlerResult
110 dhcpcd_message(_unused DBusConnection *bus, DBusMessage *msg, void *data)
112 if (dhcpcd_dispatch_message((DHCPCD_CONNECTION *)data, msg))
113 return DBUS_HANDLER_RESULT_HANDLED;
114 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
118 dhcpcd_if_free(DHCPCD_IF *ifs)
122 while (ifs != NULL) {
130 dhcpcd_open(char **error)
133 DHCPCD_CONNECTION *con;
137 dbus_error_init(&err);
138 bus = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
139 if (dbus_error_is_set(&err)) {
141 *error = strdup(err.message);
142 dbus_error_free(&err);
147 con = calloc(1, sizeof(*con));
151 if (!dbus_connection_set_watch_functions(bus,
152 dhcpcd_add_watch, dhcpcd_delete_watch, NULL, con, NULL))
154 dbus_bus_add_match(bus,
155 "type='signal',interface='" DHCPCD_SERVICE "'", &err);
156 dbus_connection_flush(bus);
157 if (dbus_error_is_set(&err)) {
159 *error = strdup(err.message);
160 dbus_error_free(&err);
163 if (!dbus_connection_add_filter(bus, dhcpcd_message, con, NULL))
166 /* Give dhcpcd-dbus a little time to automatically start */
168 while (--tries > 0) {
169 if (dhcpcd_command(con, "GetVersion", NULL, NULL)) {
170 dhcpcd_error_clear(con);
175 con->next = dhcpcd_connections;
176 dhcpcd_connections = con;
182 dbus_connection_unref(bus);
187 dhcpcd_close(DHCPCD_CONNECTION *con)
189 DHCPCD_CONNECTION *c, *l;
192 for (c = dhcpcd_connections; c; c = c->next) {
195 dhcpcd_connections = con->next;
198 dbus_connection_unref(con->bus);
199 dhcpcd_if_free(con->interfaces);
200 dhcpcd_wi_history_clear(con);
208 dhcpcd_error_set(con, NULL, ESRCH);
213 dhcpcd_if_connection(DHCPCD_IF *interface)
215 DHCPCD_CONNECTION *c;
218 for (c = dhcpcd_connections; c; c = c->next) {
219 for (i = c->interfaces; i; i = i->next)
227 dhcpcd_error_clear(DHCPCD_CONNECTION *con)
235 dhcpcd_error_set(DHCPCD_CONNECTION *con, const char *error, int err)
237 dhcpcd_error_clear(con);
239 con->error = strdup(error);
243 con->error = strdup(strerror(err));
249 dhcpcd_error(DHCPCD_CONNECTION *con)
255 dhcpcd_iter_get(DHCPCD_CONNECTION *con, DBusMessageIter *iter,
258 if (dbus_message_iter_get_arg_type(iter) == type) {
259 dbus_message_iter_get_basic(iter, arg);
260 dbus_message_iter_next(iter);
263 dhcpcd_error_set(con, 0, EINVAL);
268 dhcpcd_message_error(DHCPCD_CONNECTION *con, DHCPCD_MESSAGE *msg)
270 DBusMessageIter args;
273 if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_ERROR)
275 if (dbus_message_iter_init(msg, &args) &&
276 dbus_message_iter_get_arg_type(&args) == DBUS_TYPE_STRING)
278 dbus_message_iter_get_basic(&args, &s);
279 dhcpcd_error_set(con, s, 0);
285 dhcpcd_send_reply(DHCPCD_CONNECTION *con, DHCPCD_MESSAGE *msg)
288 DBusPendingCall *pending;
291 if (!dbus_connection_send_with_reply(con->bus, msg, &pending,
292 DHCPCD_TIMEOUT_MS) ||
295 dbus_connection_flush(con->bus);
297 /* Block until we receive a reply */
298 dbus_pending_call_block(pending);
299 reply = dbus_pending_call_steal_reply(pending);
300 dbus_pending_call_unref(pending);
301 if (dhcpcd_message_error(con, reply)) {
302 dbus_message_unref(reply);
309 dhcpcd_message_reply(DHCPCD_CONNECTION *con, const char *cmd, const char *arg)
311 DBusMessage *msg, *reply;
312 DBusMessageIter args;
314 msg = dbus_message_new_method_call(DHCPCD_SERVICE, DHCPCD_PATH,
315 DHCPCD_SERVICE, cmd);
317 dhcpcd_error_set(con, 0, errno);
320 dbus_message_iter_init_append(msg, &args);
322 !dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &arg))
324 dbus_message_unref(msg);
325 dhcpcd_error_set(con, 0, EINVAL);
328 reply = dhcpcd_send_reply(con, msg);
329 dbus_message_unref(msg);
334 dhcpcd_command(DHCPCD_CONNECTION *con, const char *cmd, const char *arg,
338 DBusMessageIter args;
341 msg = dhcpcd_message_reply(con, cmd, arg);
344 if (dhcpcd_message_error(con, msg)) {
345 dbus_message_unref(msg);
349 dbus_message_iter_init(msg, &args) &&
350 dbus_message_iter_get_arg_type(&args) == DBUS_TYPE_STRING)
352 dbus_message_iter_get_basic(&args, &s);
355 dbus_message_unref(msg);
360 dhcpcd_dispatch(int fd)
367 fds.events = (POLLIN | POLLHUP | POLLOUT | POLLERR);
368 n = poll(&fds, 1, 0);
371 if (fds.revents & POLLIN)
372 flags |= DBUS_WATCH_READABLE;
373 if (fds.revents & POLLOUT)
374 flags |= DBUS_WATCH_WRITABLE;
375 if (fds.revents & POLLHUP)
376 flags |= DBUS_WATCH_HANGUP;
377 if (fds.revents & POLLERR)
378 flags |= DBUS_WATCH_ERROR;
380 for (w = dhcpcd_watching; w; w = w->next) {
381 if (w->pollfd.fd == fd) {
383 dbus_watch_handle(w->watch, flags);
384 dbus_connection_ref(w->connection->bus);
385 while (dbus_connection_dispatch(w->connection->bus) ==
386 DBUS_DISPATCH_DATA_REMAINS)
388 dbus_connection_unref(w->connection->bus);
394 dhcpcd_if_new(DHCPCD_CONNECTION *con, DBusMessageIter *array, char **order)
396 DBusMessageIter dict, entry, var;
402 if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY) {
406 dbus_message_iter_recurse(array, &dict);
407 i = calloc(1, sizeof(*i));
409 dhcpcd_error_set(con, 0, errno);
412 errors = con->errors;
414 dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY;
415 dbus_message_iter_next(&dict))
417 dbus_message_iter_recurse(&dict, &entry);
418 if (!dhcpcd_iter_get(con, &entry, DBUS_TYPE_STRING, &s))
420 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
422 dbus_message_iter_recurse(&entry, &var);
423 if (strcmp(s, "Interface") == 0) {
424 if (!dhcpcd_iter_get(con, &var, DBUS_TYPE_STRING, &s))
426 strlcpy(i->ifname, s, sizeof(i->ifname));
427 } else if (strcmp(s, "Flags") == 0) {
428 if (!dhcpcd_iter_get(con, &var, DBUS_TYPE_UINT32, &u32))
431 } else if (strcmp(s, "Reason") == 0) {
432 if (!dhcpcd_iter_get(con, &var, DBUS_TYPE_STRING, &s))
434 strlcpy(i->reason, s, sizeof(i->reason));
435 } else if (strcmp(s, "Wireless") == 0) {
436 /* b is an int as DBus booleans want more space than
438 if (!dhcpcd_iter_get(con, &var, DBUS_TYPE_BOOLEAN, &b))
441 } else if (strcmp(s, "SSID") == 0) {
442 if (!dhcpcd_iter_get(con, &var, DBUS_TYPE_STRING, &s))
444 strlcpy(i->ssid, s, sizeof(i->ssid));
445 } else if (strcmp(s, "IPAddress") == 0) {
446 if (!dhcpcd_iter_get(con, &var, DBUS_TYPE_UINT32, &u32))
449 } else if (strcmp(s, "SubnetCIDR") == 0)
450 dbus_message_iter_get_basic(&var, &i->cidr);
451 else if (order != NULL && strcmp(s, "InterfaceOrder") == 0)
452 if (!dhcpcd_iter_get(con, &var, DBUS_TYPE_STRING, order))
456 if (dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_INVALID) {
458 if (con->errors == errors)
459 dhcpcd_error_set(con, NULL, EINVAL);
466 dhcpcd_interfaces(DHCPCD_CONNECTION *con)
469 DBusMessageIter args, dict, entry;
473 if (con->interfaces != NULL)
474 return con->interfaces;
476 msg = dhcpcd_message_reply(con, "GetInterfaces", NULL);
479 if (!dbus_message_iter_init(msg, &args) ||
480 dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY)
482 dbus_message_unref(msg);
483 dhcpcd_error_set(con, 0, EINVAL);
488 errors = con->errors;
489 dbus_message_iter_recurse(&args, &dict);
491 dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY;
492 dbus_message_iter_next(&dict))
494 dbus_message_iter_recurse(&dict, &entry);
495 dbus_message_iter_next(&entry);
496 i = dhcpcd_if_new(con, &entry, NULL);
505 dbus_message_unref(msg);
506 if (dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_INVALID) {
507 if (con->errors == errors)
508 dhcpcd_error_set(con, 0, EINVAL);
509 dhcpcd_if_free(con->interfaces);
510 con->interfaces = NULL;
512 return con->interfaces;
516 dhcpcd_if_find(DHCPCD_CONNECTION *con, const char *ifname)
520 if (con->interfaces == NULL)
521 dhcpcd_interfaces(con);
522 for (i = con->interfaces; i; i = i ->next)
523 if (strcmp(i->ifname, ifname) == 0)
529 dhcpcd_status(DHCPCD_CONNECTION *con)
531 if (con->status == NULL)
532 dhcpcd_command(con, "GetStatus", NULL, &con->status);
537 dhcpcd_set_watch_functions(DHCPCD_CONNECTION *con,
538 void (*add_watch)(DHCPCD_CONNECTION *, const struct pollfd *, void *),
539 void (*delete_watch)(DHCPCD_CONNECTION *, const struct pollfd *, void *),
544 con->add_watch = add_watch;
545 con->delete_watch = delete_watch;
546 con->watch_data = data;
547 if (con->add_watch) {
548 for (w = dhcpcd_watching; w; w = w->next)
549 if (w->connection == con)
550 con->add_watch(con, &w->pollfd, data);
555 dhcpcd_set_signal_functions(DHCPCD_CONNECTION *con,
556 void (*event)(DHCPCD_CONNECTION *, DHCPCD_IF *, void *),
557 void (*status_changed)(DHCPCD_CONNECTION *, const char *, void *),
558 void (*wi_scanresults)(DHCPCD_CONNECTION *, DHCPCD_IF *, void *),
564 con->status_changed = status_changed;
565 con->wi_scanresults = wi_scanresults;
566 con->signal_data = data;
567 if (con->status_changed) {
568 if (con->status == NULL)
570 con->status_changed(con, con->status, data);
572 if (con->wi_scanresults) {
573 for (i = dhcpcd_interfaces(con); i; i = i->next)
575 con->wi_scanresults(con, i, data);