summaryrefslogtreecommitdiff
path: root/src/freebsd/dkp-devd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/freebsd/dkp-devd.c')
-rw-r--r--src/freebsd/dkp-devd.c245
1 files changed, 245 insertions, 0 deletions
diff --git a/src/freebsd/dkp-devd.c b/src/freebsd/dkp-devd.c
new file mode 100644
index 0000000..352a1b9
--- /dev/null
+++ b/src/freebsd/dkp-devd.c
@@ -0,0 +1,245 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * dkp-devd.c : process devd events
+ *
+ * Copyright (C) 2006, 2007 Joe Marcus Clarke <marcus@FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ **************************************************************************/
+
+#include "config.h"
+
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "egg-debug.h"
+
+#include "dkp-backend.h"
+#include "dkp-backend-acpi.h"
+#include "dkp-devd.h"
+
+#define DKP_DEVD_SOCK_PATH "/var/run/devd.pipe"
+
+#define DKP_DEVD_EVENT_NOTIFY '!'
+#define DKP_DEVD_EVENT_ADD '+'
+#define DKP_DEVD_EVENT_REMOVE '-'
+#define DKP_DEVD_EVENT_NOMATCH '?'
+
+static DkpDevdHandler *handlers[] = {
+ &dkp_backend_acpi_devd_handler,
+};
+
+static gboolean dkp_devd_inited = FALSE;
+
+#if 0
+static GHashTable *
+dkp_devd_parse_params (const char *str)
+{
+ GHashTable *params;
+ char **pairs;
+ int i;
+
+ g_return_val_if_fail(str != NULL, FALSE);
+
+ params = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+
+ pairs = g_strsplit(str, " ", 0);
+ for (i = 0; pairs[i]; i++) {
+ char *equal;
+
+ equal = strchr(pairs[i], '=');
+ g_hash_table_insert(params,
+ equal ? g_strndup(pairs[i], equal - pairs[i]) : g_strdup(pairs[i]),
+ equal ? g_strdup(equal + 1) : NULL);
+ }
+ g_strfreev(pairs);
+
+ return params;
+}
+#endif
+
+static gboolean
+dkp_devd_parse_notify (const char *event,
+ char **system,
+ char **subsystem,
+ char **type,
+ char **data)
+{
+ char **items;
+ gboolean status = FALSE;
+
+ /*
+ !system=IFNET subsystem=bge0 type=LINK_DOWN
+ */
+
+ g_return_val_if_fail(event != NULL, FALSE);
+ g_return_val_if_fail(system != NULL, FALSE);
+ g_return_val_if_fail(subsystem != NULL, FALSE);
+ g_return_val_if_fail(type != NULL, FALSE);
+ g_return_val_if_fail(data != NULL, FALSE);
+
+ items = g_strsplit(event, " ", 0);
+ if (g_strv_length(items) < 3)
+ goto end;
+
+ if (! g_str_has_prefix(items[0], "system=") ||
+ ! g_str_has_prefix(items[1], "subsystem=") ||
+ ! g_str_has_prefix(items[2], "type="))
+ goto end;
+
+ *system = g_strdup(items[0] + 7);
+ *subsystem = g_strdup(items[1] + 10);
+ *type = g_strdup(items[2] + 5);
+ *data = g_strdup(items[3]); /* may be NULL */
+
+ status = TRUE;
+
+end:
+ g_strfreev(items);
+ return status;
+}
+
+static void
+dkp_devd_process_notify_event (DkpBackend *backend,
+ const char *system,
+ const char *subsystem,
+ const char *type,
+ const char *data)
+{
+ int i;
+
+ g_return_if_fail(backend != NULL);
+ g_return_if_fail(system != NULL);
+ g_return_if_fail(subsystem != NULL);
+ g_return_if_fail(type != NULL);
+ g_return_if_fail(data != NULL);
+
+ for (i = 0; i < (int) G_N_ELEMENTS(handlers); i++)
+ if (handlers[i]->notify && handlers[i]->notify(backend, system, subsystem, type, data))
+ return;
+
+ /* no default action */
+}
+
+static void
+dkp_devd_process_event (const char *event, gpointer user_data)
+{
+ DkpBackend *backend;
+
+ g_return_if_fail(event != NULL);
+
+ backend = DKP_BACKEND(user_data);
+
+ egg_debug("received devd event: '%s'", event);
+
+ switch (event[0]) {
+ case DKP_DEVD_EVENT_ADD:
+ case DKP_DEVD_EVENT_REMOVE:
+ /* Do nothing as we don't currently hotplug ACPI devices. */
+ return;
+
+ case DKP_DEVD_EVENT_NOTIFY: {
+ char *system;
+ char *subsystem;
+ char *type;
+ char *data;
+
+ if (! dkp_devd_parse_notify(event + 1, &system, &subsystem, &type, &data))
+ goto malformed;
+
+ dkp_devd_process_notify_event(backend, system, subsystem, type, data);
+
+ g_free(system);
+ g_free(subsystem);
+ g_free(type);
+ g_free(data);
+ }
+ return;
+
+ case DKP_DEVD_EVENT_NOMATCH:
+ /* Do nothing. */
+ return;
+ }
+
+malformed:
+ egg_warning("malformed devd event: %s", event);
+}
+
+static gboolean
+dkp_devd_event_cb (GIOChannel *source, GIOCondition condition,
+ gpointer user_data)
+{
+ char *event;
+ gsize terminator;
+ GIOStatus status;
+
+ status = g_io_channel_read_line(source, &event, NULL, &terminator, NULL);
+
+ if (status == G_IO_STATUS_NORMAL) {
+ event[terminator] = 0;
+ dkp_devd_process_event(event, user_data);
+ g_free(event);
+ } else if (status == G_IO_STATUS_AGAIN) {
+ dkp_devd_init(DKP_BACKEND(user_data));
+ if (dkp_devd_inited) {
+ int fd;
+
+ fd = g_io_channel_unix_get_fd(source);
+ g_io_channel_shutdown(source, FALSE, NULL);
+ close(fd);
+
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+void
+dkp_devd_init (DkpBackend *backend)
+{
+ int event_fd;
+ struct sockaddr_un addr;
+
+ event_fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (event_fd < 0) {
+ egg_warning("failed to create event socket: '%s'", g_strerror(errno));
+ dkp_devd_inited = FALSE;
+ return;
+ }
+
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, DKP_DEVD_SOCK_PATH, sizeof(addr.sun_path));
+ if (connect(event_fd, (struct sockaddr *)&addr, sizeof(addr)) == 0) {
+ GIOChannel *channel;
+
+ channel = g_io_channel_unix_new(event_fd);
+ g_io_add_watch(channel, G_IO_IN, dkp_devd_event_cb, backend);
+ g_io_channel_unref(channel);
+ dkp_devd_inited = TRUE;
+ } else {
+ egg_warning("failed to connect to %s: '%s'", DKP_DEVD_SOCK_PATH,
+ g_strerror(errno));
+ close(event_fd);
+ dkp_devd_inited = FALSE;
+ }
+}