mapper-cli: rewrite with sdbus
There are too many instances of the wait/call applications
spawned during BMC startup. Re-write using sdbus.
Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
Change-Id: Ia6fb3b74cb70f93cfd5cc57c1a8b7a9aa2d944c4
diff --git a/libmapper/Makefile b/libmapper/Makefile
index 806390c..2f71515 100644
--- a/libmapper/Makefile
+++ b/libmapper/Makefile
@@ -1,16 +1,18 @@
libdir=/usr/lib
+sbindir=/usr/sbin
includedir=/usr/include
PACKAGE_DEPS=libsystemd
SONAME=libmapper.so
VERSION=1
LIBMAPPER=$(SONAME).$(VERSION)
+BIN=mapper
INCLUDES=mapper.h
LDLIBS+=$(shell pkg-config --libs $(PACKAGE_DEPS))
ALL_CFLAGS+=$(shell pkg-config --cflags $(PACKAGE_DEPS)) -fPIC -Werror $(CFLAGS)
-all: $(SONAME)
+all: $(SONAME) $(BIN)
%.o: %.c
$(CC) -c $(ALL_CFLAGS) -o $@ $<
@@ -22,12 +24,17 @@
$(CC) -shared $(CFLAGS) $(LDFLAGS) -Wl,-soname,$(SONAME) \
-o $@ $^ $(LDLIBS)
-install: $(SONAME) $(LIBMAPPER)
+$(BIN): app.o $(LIBMAPPER)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+install: $(SONAME) $(LIBMAPPER) $(BIN)
@mkdir -p $(DESTDIR)$(includedir)
install $(INCLUDES) $(DESTDIR)$(includedir)
@mkdir -p $(DESTDIR)$(libdir)
install $(LIBMAPPER) $(DESTDIR)$(libdir)
ln -sf $(LIBMAPPER) $(DESTDIR)$(libdir)/$(SONAME)
+ @mkdir -p $(DESTDIR)$(sbindir)
+ install $(BIN) $(DESTDIR)$(sbindir)
clean:
- rm -f *.o $(LIBMAPPER) $(SONAME)
+ rm -f *.o $(LIBMAPPER) $(SONAME) $(BIN)
diff --git a/libmapper/app.c b/libmapper/app.c
new file mode 100644
index 0000000..e2b94eb
--- /dev/null
+++ b/libmapper/app.c
@@ -0,0 +1,159 @@
+/**
+ * Copyright © 2016 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <systemd/sd-bus.h>
+#include <systemd/sd-event.h>
+#include "mapper.h"
+
+static int call_main(int argc, char *argv[])
+{
+ int r;
+ sd_bus *conn = NULL;
+ char *service = NULL;
+ sd_bus_message *m = NULL, *reply = NULL;
+ sd_bus_error error = SD_BUS_ERROR_NULL;
+
+ if(argc < 5) {
+ fprintf(stderr, "Usage: %s call OBJECTPATH INTERFACE "
+ "METHOD [SIGNATURE [ARGUMENT...]\n", argv[0]);
+ r = -1;
+ goto finish;
+ }
+
+ r = sd_bus_default_system(&conn);
+ if(r < 0) {
+ fprintf(stderr, "Error connecting to system bus: %s\n",
+ strerror(-r));
+ goto finish;
+ }
+
+ r = mapper_get_service(conn, argv[2], &service);
+ if(r < 0) {
+ fprintf(stderr, "Error finding service: %s\n",
+ strerror(-r));
+ goto finish;
+ }
+
+ r = sd_bus_message_new_method_call(
+ conn, &m, service, argv[2], argv[3], argv[4]);
+ if(r < 0) {
+ fprintf(stderr, "Error populating message: %s\n",
+ strerror(-r));
+ goto finish;
+ }
+
+ if(argc > 5) {
+ char **p;
+ p = argv + 6;
+ r = sd_bus_message_append_cmdline(m, argv[5], &p);
+ if(r < 0) {
+ fprintf(stderr, "Error appending method arguments: %s\n",
+ strerror(-r));
+ goto finish;
+ }
+ }
+
+ r = sd_bus_call(conn, m, 0, &error, &reply);
+ if(r < 0) {
+ fprintf(stderr, "Error invoking method: %s\n",
+ strerror(-r));
+ goto finish;
+ }
+
+finish:
+ exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+static void quit(int r, void *loop)
+{
+ sd_event_exit((sd_event *)loop, r);
+}
+
+static int wait_main(int argc, char *argv[])
+{
+ int r;
+ sd_bus *conn = NULL;
+ sd_event *loop = NULL;
+ mapper_async_wait *wait = NULL;
+
+ if(argc < 3) {
+ fprintf(stderr, "Usage: %s wait OBJECTPATH...\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ r = sd_bus_default_system(&conn);
+ if(r < 0) {
+ fprintf(stderr, "Error connecting to system bus: %s\n",
+ strerror(-r));
+ goto finish;
+ }
+
+ r = sd_event_default(&loop);
+ if (r < 0) {
+ fprintf(stderr, "Error obtaining event loop: %s\n",
+ strerror(-r));
+
+ goto finish;
+ }
+
+ r = sd_bus_attach_event(conn, loop, SD_EVENT_PRIORITY_NORMAL);
+ if (r < 0) {
+ fprintf(stderr, "Failed to attach system "
+ "bus to event loop: %s\n",
+ strerror(-r));
+ goto finish;
+ }
+
+ r = mapper_wait_async(conn, argv+2, quit, loop, &wait);
+ if(r < 0) {
+ fprintf(stderr, "Error configuring waitlist: %s\n",
+ strerror(-r));
+ goto finish;
+ }
+
+ r = sd_event_loop(loop);
+ if(r < 0) {
+ fprintf(stderr, "Error starting event loop: %s\n",
+ strerror(-r));
+ goto finish;
+ }
+
+finish:
+ exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char *argv[])
+{
+ static const char *usage =
+ "Usage: %s {COMMAND} ...\n"
+ "\nCOMMANDS:\n"
+ " call invoke the specified method\n"
+ " wait wait for the specified objects to appear on the DBus\n";
+
+ if(argc < 2) {
+ fprintf(stderr, usage, argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if(!strcmp(argv[1], "call"))
+ call_main(argc, argv);
+ if(!strcmp(argv[1], "wait"))
+ wait_main(argc, argv);
+
+ fprintf(stderr, usage, argv[0]);
+ exit(EXIT_FAILURE);
+}
diff --git a/libmapper/mapper.c b/libmapper/mapper.c
index 62dd2e6..3646cc1 100644
--- a/libmapper/mapper.c
+++ b/libmapper/mapper.c
@@ -13,10 +13,319 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include <stdlib.h>
#include <string.h>
+#include <stdio.h>
+#include <errno.h>
#include <systemd/sd-bus.h>
#include "mapper.h"
+static const char *async_wait_name_owner_match =
+ "type='signal',"
+ "sender='org.freedesktop.DBus',"
+ "interface='org.freedesktop.DBus',"
+ "member='NameOwnerChanged',"
+ "path='/org/freedesktop/DBus'";
+
+static const char *async_wait_interfaces_added_match =
+ "type='signal',"
+ "interface='org.freedesktop.DBus.ObjectManager',"
+ "member='InterfacesAdded'";
+
+struct mapper_async_wait
+{
+ char **objs;
+ void (*callback)(int, void *);
+ void *userdata;
+ sd_bus *conn;
+ sd_bus_slot *name_owner_slot;
+ sd_bus_slot *intf_slot;
+ int *status;
+ int count;
+ int finished;
+ int r;
+};
+
+struct async_wait_callback_data
+{
+ mapper_async_wait *wait;
+ const char *path;
+};
+
+static int async_wait_match_name_owner_changed(sd_bus_message *, void *,
+ sd_bus_error *);
+static int async_wait_match_interfaces_added(sd_bus_message *, void *,
+ sd_bus_error *);
+static int async_wait_check_done(mapper_async_wait *);
+static void async_wait_done(int r, mapper_async_wait *);
+static int async_wait_get_objects(mapper_async_wait *);
+
+static int sarraylen(char *array[])
+{
+ int count = 0;
+ char **p = array;
+
+ while(*p != NULL) {
+ ++count;
+ ++p;
+ }
+
+ return count;
+}
+
+static void sarrayfree(char *array[])
+{
+ char **p = array;
+ while(*p != NULL) {
+ free(*p);
+ ++p;
+ }
+ free(array);
+}
+
+static char **sarraydup(char *array[])
+{
+ int count = sarraylen(array);
+ int i;
+ char **ret = NULL;
+
+ ret = malloc(sizeof(*ret) * count);
+ if(!ret)
+ return NULL;
+
+ for(i=0; i<count; ++i) {
+ ret[i] = strdup(array[i]);
+ if(!ret[i])
+ goto error;
+ }
+
+ return ret;
+
+error:
+ sarrayfree(ret);
+ return NULL;
+}
+
+static int async_wait_getobject_callback(sd_bus_message *m,
+ void *userdata,
+ sd_bus_error *e)
+{
+ int i, r;
+ const char *msg;
+ struct async_wait_callback_data *data = userdata;
+ mapper_async_wait *wait = data->wait;
+
+ if(wait->finished)
+ return 0;
+ if(sd_bus_message_get_errno(m))
+ return 0;
+
+ for(i=0; i<wait->count; ++i) {
+ if(!strcmp(data->path, wait->objs[i])) {
+ wait->status[i] = 1;
+ }
+ }
+
+ free(data);
+ if(async_wait_check_done(wait))
+ async_wait_done(0, wait);
+
+ return 0;
+}
+
+static int async_wait_get_objects(mapper_async_wait *wait)
+{
+ int i, r;
+ struct async_wait_callback_data *data = NULL;
+
+ for(i=0; i<wait->count; ++i) {
+ if(wait->status[i])
+ continue;
+ data = malloc(sizeof(*data));
+ data->wait = wait;
+ data->path = wait->objs[i];
+ r = sd_bus_call_method_async(
+ wait->conn,
+ NULL,
+ "org.openbmc.ObjectMapper",
+ "/org/openbmc/ObjectMapper",
+ "org.openbmc.ObjectMapper",
+ "GetObject",
+ async_wait_getobject_callback,
+ data,
+ "s",
+ wait->objs[i]);
+ if(r < 0) {
+ free(data);
+ fprintf(stderr, "Error invoking method: %s\n",
+ strerror(-r));
+ return r;
+ }
+ }
+
+ return 0;
+}
+
+static int async_wait_match_name_owner_changed(sd_bus_message *m, void *w,
+ sd_bus_error *e)
+{
+ int i, r;
+
+ mapper_async_wait *wait = w;
+ if(wait->finished)
+ return 0;
+
+ r = async_wait_get_objects(wait);
+ if(r < 0)
+ async_wait_done(r, wait);
+
+ return 0;
+}
+
+static int async_wait_match_interfaces_added(sd_bus_message *m, void *w,
+ sd_bus_error *e)
+{
+ int i, r;
+ mapper_async_wait *wait = w;
+ const char *path;
+
+ if(wait->finished)
+ return 0;
+
+ r = sd_bus_message_read(m, "o", &path);
+ if (r < 0) {
+ fprintf(stderr, "Error reading message: %s\n",
+ strerror(-r));
+ goto finished;
+ }
+
+ for(i=0; i<wait->count; ++i) {
+ if(!strcmp(path, wait->objs[i]))
+ wait->status[i] = 1;
+ }
+
+finished:
+ if(r < 0 || async_wait_check_done(wait))
+ async_wait_done(r < 0 ? r : 0, wait);
+
+ return 0;
+}
+
+static void async_wait_done(int r, mapper_async_wait *w)
+{
+ if(w->finished)
+ return;
+
+ w->finished = 1;
+ sd_bus_slot_unref(w->name_owner_slot);
+ sd_bus_slot_unref(w->intf_slot);
+
+ if(w->callback)
+ w->callback(r, w->userdata);
+}
+
+static int async_wait_check_done(mapper_async_wait *w)
+{
+ int i;
+
+ if(w->finished)
+ return 1;
+
+ for(i=0; i<w->count; ++i)
+ if(!w->status[i])
+ return 0;
+
+ return 1;
+}
+
+void mapper_wait_async_free(mapper_async_wait *w)
+{
+ free(w->status);
+ sarrayfree(w->objs);
+ free(w);
+}
+
+int mapper_wait_async(sd_bus *conn,
+ char *objs[],
+ void (*callback)(int, void *),
+ void *userdata,
+ mapper_async_wait **w)
+{
+ int r;
+ mapper_async_wait *wait = NULL;
+
+ wait = malloc(sizeof(*wait));
+ if(!wait)
+ return -ENOMEM;
+
+ memset(wait, 0, sizeof(*wait));
+ wait->conn = conn;
+ wait->callback = callback;
+ wait->userdata = userdata;
+ wait->count = sarraylen(objs);
+ if(!wait->count)
+ return 0;
+
+ wait->objs = sarraydup(objs);
+ if(!wait->objs) {
+ r = -ENOMEM;
+ goto free_wait;
+ }
+
+ wait->status = malloc(sizeof(*wait->status) * wait->count);
+ if(!wait->status) {
+ r = -ENOMEM;
+ goto free_objs;
+ }
+ memset(wait->status, 0, sizeof(*wait->status) * wait->count);
+
+ r = sd_bus_add_match(conn,
+ &wait->name_owner_slot,
+ async_wait_name_owner_match,
+ async_wait_match_name_owner_changed,
+ wait);
+ if(r < 0) {
+ fprintf(stderr, "Error adding match rule: %s\n",
+ strerror(-r));
+ goto free_status;
+ }
+
+ r = sd_bus_add_match(conn,
+ &wait->intf_slot,
+ async_wait_interfaces_added_match,
+ async_wait_match_interfaces_added,
+ wait);
+ if(r < 0) {
+ fprintf(stderr, "Error adding match rule: %s\n",
+ strerror(-r));
+ goto unref_name_slot;
+ }
+
+ r = async_wait_get_objects(wait);
+ if(r < 0) {
+ fprintf(stderr, "Error calling method: %s\n",
+ strerror(-r));
+ goto unref_intf_slot;
+ }
+
+ *w = wait;
+
+ return 0;
+
+unref_intf_slot:
+ sd_bus_slot_unref(wait->intf_slot);
+unref_name_slot:
+ sd_bus_slot_unref(wait->name_owner_slot);
+free_status:
+ free(wait->status);
+free_objs:
+ sarrayfree(wait->objs);
+free_wait:
+ free(wait);
+
+ return r;
+}
+
int mapper_get_service(sd_bus *conn, const char *obj, char **service)
{
sd_bus_error error = SD_BUS_ERROR_NULL;
diff --git a/libmapper/mapper.h b/libmapper/mapper.h
index 58f1844..b49b302 100644
--- a/libmapper/mapper.h
+++ b/libmapper/mapper.h
@@ -3,6 +3,11 @@
#ifdef __cplusplus
extern "C" {
#endif
+typedef struct mapper_async_wait mapper_async_wait;
+void mapper_wait_async_free(mapper_async_wait *);
+
+int mapper_wait_async(sd_bus *, char *[],
+ void (*)(int, void *), void *, mapper_async_wait **);
int mapper_get_service(sd_bus *conn, const char *obj, char **service);
#ifdef __cplusplus
}