From: David Disseldorp Date: Tue, 2 Oct 2012 14:42:48 +0000 (+0200) Subject: Add snapper dbus client example C code X-Git-Tag: v0.1.3~87 X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=d3f484ff594550400321c07bc087009d41914f47;p=thirdparty%2Fsnapper.git Add snapper dbus client example C code Connect to snapperd over dbus and dispatch requests. Requests are manually unmarshalled based on the response signature. Currently only ListSnapshots and ListConfigs commands are supported. --- diff --git a/configure.in b/configure.in index bbdf04e7..05b85dfb 100644 --- a/configure.in +++ b/configure.in @@ -69,6 +69,7 @@ AC_OUTPUT( Makefile snapper/Makefile examples/Makefile + examples/C/Makefile bindings/Makefile bindings/python/Makefile dbus/Makefile diff --git a/examples/C/.gitignore b/examples/C/.gitignore new file mode 100644 index 00000000..f3ef8091 --- /dev/null +++ b/examples/C/.gitignore @@ -0,0 +1,2 @@ +csnap +snapper_dbus_cli.o diff --git a/examples/C/Makefile.am b/examples/C/Makefile.am new file mode 100644 index 00000000..529ea726 --- /dev/null +++ b/examples/C/Makefile.am @@ -0,0 +1,13 @@ +# +# Makefile.am for snapper/examples/C +# + +INCLUDES = -I$(top_srcdir) -I/usr/include/dbus-1.0 -I$(libdir)/dbus-1.0/include + +noinst_PROGRAMS = csnap + +csnap_SOURCES = \ + snapper_dbus_cli.c + +csnap_LDADD = -ldbus-1 + diff --git a/examples/C/snapper_dbus_cli.c b/examples/C/snapper_dbus_cli.c new file mode 100644 index 00000000..60945d73 --- /dev/null +++ b/examples/C/snapper_dbus_cli.c @@ -0,0 +1,749 @@ +/* + * Copyright (c) [2011-2012] Novell, Inc. + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * 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, contact Novell, Inc. + * + * To contact Novell about this file by physical or electronic mail, you may + * find current contact information at www.novell.com. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define CDBUS_SIG_LIST_SNAPS_RSP "a(uqutussa{ss})" +#define CDBUS_SIG_LIST_CONFS_RSP "a(ssa{ss})" + +struct dict { + char *key; + char *val; +}; + +struct snap { + uint32_t id; + uint16_t type; + uint32_t pre_id; + uint64_t time; + uint32_t creator_uid; + char *desc; + char *cleanup; + uint32_t num_user_data; + struct dict *user_data; +}; + +struct config { + char *name; + char *mnt; + uint32_t num_attrs; + struct dict *attrs; +}; + +static DBusConnection *cdbus_conn(void) +{ + DBusError err; + DBusConnection *conn; + + dbus_error_init(&err); + + /* bus connection */ + conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err); + if (dbus_error_is_set(&err)) { + fprintf(stderr, "connection error: %s\n", err.message); + dbus_error_free(&err); + } + if (NULL == conn) { + return NULL; + } + + return conn; +} + +static int cdbus_msg_send(DBusConnection *conn, + DBusMessage *msg, + DBusPendingCall **pend_out) +{ + DBusPendingCall *pending; + + /* send message and get a handle for a reply */ + if (!dbus_connection_send_with_reply(conn, msg, &pending, -1)) { + return -ENOMEM; + } + if (NULL == pending) { + fprintf(stderr, "Pending Call Null\n"); + return -EINVAL; + } + + dbus_connection_flush(conn); + *pend_out = pending; + + return 0; +} + +static int cdbus_msg_recv(DBusConnection *conn, + DBusPendingCall *pending, + DBusMessage **msg_out) +{ + DBusMessage *msg; + + /* block until we receive a reply */ + dbus_pending_call_block(pending); + + /* get the reply message */ + msg = dbus_pending_call_steal_reply(pending); + if (msg == NULL) { + fprintf(stderr, "Reply Null\n"); + return -ENOMEM; + } + /* free the pending message handle */ + dbus_pending_call_unref(pending); + *msg_out = msg; + + return 0; +} + + +static int cdbus_list_snaps_pack(char *snapper_conf, + DBusMessage **req_msg_out) +{ + DBusMessage *msg; + DBusMessageIter args; + + msg = dbus_message_new_method_call("org.opensuse.Snapper", /* target for the method call */ + "/org/opensuse/Snapper", /* object to call on */ + "org.opensuse.Snapper", /* interface to call on */ + "ListSnapshots"); /* method name */ + if (NULL == msg) { + fprintf(stderr, "Message Null\n"); + return -ENOMEM; + } + + /* append arguments */ + dbus_message_iter_init_append(msg, &args); + if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, + &snapper_conf)) { + fprintf(stderr, "Out Of Memory!\n"); + return -ENOMEM; + } + + *req_msg_out = msg; + + return 0; +} + +static int cdbus_type_check(DBusMessageIter *iter, int expected_type) +{ + int type = dbus_message_iter_get_arg_type(iter); + if (type != expected_type) { + fprintf(stderr, "got type %d, expecting %d\n", + type, expected_type); + return -EINVAL; + } + + return 0; +} + +static int cdbus_type_check_get(DBusMessageIter *iter, int expected_type, + void *val) +{ + int ret; + ret = cdbus_type_check(iter, expected_type); + if (ret < 0) { + return ret; + } + + dbus_message_iter_get_basic(iter, val); + + return 0; +} + +static int dict_unpack(DBusMessageIter *iter, + struct dict *dict_out) + +{ + int ret; + DBusMessageIter dct_iter; + + if (cdbus_type_check(iter, DBUS_TYPE_DICT_ENTRY) < 0) { + return -EINVAL; + } + dbus_message_iter_recurse(iter, &dct_iter); + + ret = cdbus_type_check_get(&dct_iter, DBUS_TYPE_STRING, &dict_out->key); + if (ret < 0) { + return ret; + } + + dbus_message_iter_next(&dct_iter); + ret = cdbus_type_check_get(&dct_iter, DBUS_TYPE_STRING, &dict_out->val); + if (ret < 0) { + return ret; + } + + return 0; +} + +static void dict_array_print(uint32_t num_dicts, + struct dict *dicts) +{ + int i; + + for (i = 0; i < num_dicts; i++) { + printf("dict (\n" + "\tkey: %s\n" + "\tval: %s\n" + ")\n", + dicts[i].key, dicts[i].val); + } +} + +static int dict_array_unpack(DBusMessageIter *iter, + uint32_t *num_dicts_out, + struct dict **dicts_out) +{ + int ret; + DBusMessageIter array_iter; + uint32_t num_dicts; + struct dict *dicts = NULL; + + if (cdbus_type_check(iter, DBUS_TYPE_ARRAY) < 0) { + return -EINVAL; + } + dbus_message_iter_recurse(iter, &array_iter); + + num_dicts = 0; + while (dbus_message_iter_get_arg_type(&array_iter) + != DBUS_TYPE_INVALID) { + num_dicts++; + dicts = realloc(dicts, sizeof(struct dict) * num_dicts); + if (dicts == NULL) + abort(); + + ret = dict_unpack(&array_iter, &dicts[num_dicts - 1]); + if (ret < 0) { + free(dicts); + return ret; + } + dbus_message_iter_next(&array_iter); + } + + *num_dicts_out = num_dicts; + *dicts_out = dicts; + + return 0; +} + +static int snap_struct_unpack(DBusMessageIter *iter, + struct snap *snap_out) +{ + int ret; + DBusMessageIter st_iter; + + if (cdbus_type_check(iter, DBUS_TYPE_STRUCT) < 0) { + return -EINVAL; + } + dbus_message_iter_recurse(iter, &st_iter); + + ret = cdbus_type_check_get(&st_iter, DBUS_TYPE_UINT32, + &snap_out->id); + if (ret < 0) { + return ret; + } + + dbus_message_iter_next(&st_iter); + ret = cdbus_type_check_get(&st_iter, DBUS_TYPE_UINT16, + &snap_out->type); + if (ret < 0) { + return ret; + } + + dbus_message_iter_next(&st_iter); + ret = cdbus_type_check_get(&st_iter, DBUS_TYPE_UINT32, + &snap_out->pre_id); + if (ret < 0) { + return ret; + } + + dbus_message_iter_next(&st_iter); + ret = cdbus_type_check_get(&st_iter, DBUS_TYPE_UINT64, + &snap_out->time); + if (ret < 0) { + return ret; + } + + dbus_message_iter_next(&st_iter); + ret = cdbus_type_check_get(&st_iter, DBUS_TYPE_UINT32, + &snap_out->creator_uid); + if (ret < 0) { + return ret; + } + + dbus_message_iter_next(&st_iter); + ret = cdbus_type_check_get(&st_iter, DBUS_TYPE_STRING, + &snap_out->desc); + if (ret < 0) { + return ret; + } + + dbus_message_iter_next(&st_iter); + ret = cdbus_type_check_get(&st_iter, DBUS_TYPE_STRING, + &snap_out->cleanup); + if (ret < 0) { + return ret; + } + + dbus_message_iter_next(&st_iter); + ret = dict_array_unpack(&st_iter, &snap_out->num_user_data, + &snap_out->user_data); + + return ret; +} + +static void snap_array_free(int32_t num_snaps, + struct snap *snaps) +{ + int i; + + for (i = 0; i < num_snaps; i++) { + free(snaps[i].user_data); + } + free(snaps); +} + +static void snap_array_print(int32_t num_snaps, + struct snap *snaps) +{ + int i; + + for (i = 0; i < num_snaps; i++) { + printf("id: %u\n" + "type: %u\n" + "pre_id: %u\n" + "time: %lu\n" + "creator_uid: %u\n" + "desc: %s\n" + "cleanup: %s\n", + snaps[i].id, + snaps[i].type, + snaps[i].pre_id, + snaps[i].time, + snaps[i].creator_uid, + snaps[i].desc, + snaps[i].cleanup); + dict_array_print(snaps[i].num_user_data, snaps[i].user_data); + printf("---\n"); + } +} + +static int snap_array_unpack(DBusMessageIter *iter, + uint32_t *num_snaps_out, + struct snap **snaps_out) +{ + uint32_t num_snaps; + int ret; + struct snap *snaps = NULL; + DBusMessageIter array_iter; + + + if (cdbus_type_check(iter, DBUS_TYPE_ARRAY) < 0) { + return -EINVAL; + } + dbus_message_iter_recurse(iter, &array_iter); + + num_snaps = 0; + while (dbus_message_iter_get_arg_type(&array_iter) + != DBUS_TYPE_INVALID) { + num_snaps++; + snaps = realloc(snaps, sizeof(struct snap) * num_snaps); + if (snaps == NULL) + abort(); + + ret = snap_struct_unpack(&array_iter, &snaps[num_snaps - 1]); + if (ret < 0) { + free(snaps); + return ret; + } + dbus_message_iter_next(&array_iter); + } + + *num_snaps_out = num_snaps; + *snaps_out = snaps; + + return 0; +} + +static int cdbus_list_snaps_unpack(DBusConnection *conn, + DBusMessage *rsp_msg, + uint32_t *num_snaps_out, + struct snap **snaps_out) +{ + int ret; + DBusMessageIter iter; + int msg_type; + uint32_t num_snaps; + struct snap *snaps; + const char *sig; + + msg_type = dbus_message_get_type(rsp_msg); + if (msg_type == DBUS_MESSAGE_TYPE_ERROR) { + fprintf(stderr, "list_snaps error response: %s\n", + dbus_message_get_error_name(rsp_msg)); + return -EINVAL; + } + + if (msg_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) { + fprintf(stderr, "unexpected list_snaps ret type: %d\n", + msg_type); + return -EINVAL; + } + + sig = dbus_message_get_signature(rsp_msg); + if ((sig == NULL) + || (strcmp(sig, CDBUS_SIG_LIST_SNAPS_RSP) != 0)) { + fprintf(stderr, "bad list snaps response sig: %s, " + "expected: %s\n", + (sig ? sig : "NULL"), CDBUS_SIG_LIST_SNAPS_RSP); + return -EINVAL; + } + + /* read the parameters */ + if (!dbus_message_iter_init(rsp_msg, &iter)) { + fprintf(stderr, "Message has no arguments!\n"); + return -EINVAL; + } + + ret = snap_array_unpack(&iter, &num_snaps, &snaps); + if (ret < 0) { + fprintf(stderr, "failed to unpack snap array\n"); + return -EINVAL; + } + + *num_snaps_out = num_snaps; + *snaps_out = snaps; + + return 0; +} + +static int cdbus_list_snaps_call(DBusConnection *conn) +{ + int ret; + DBusMessage *req_msg; + DBusMessage *rsp_msg; + DBusPendingCall *pending; + uint32_t num_snaps = 0; + struct snap *snaps = NULL; + + ret = cdbus_list_snaps_pack("root", &req_msg); + if (ret < 0) { + fprintf(stderr, "failed to pack list snaps request\n"); + return ret; + } + + ret = cdbus_msg_send(conn, req_msg, &pending); + if (ret < 0) { + dbus_message_unref(req_msg); + return ret; + } + + ret = cdbus_msg_recv(conn, pending, &rsp_msg); + if (ret < 0) { + dbus_message_unref(req_msg); + dbus_pending_call_unref(pending); + return ret; + } + + ret = cdbus_list_snaps_unpack(conn, rsp_msg, + &num_snaps, &snaps); + if (ret < 0) { + fprintf(stderr, "failed to unpack list snaps response\n"); + dbus_message_unref(req_msg); + dbus_message_unref(rsp_msg); + return ret; + } + + snap_array_print(num_snaps, snaps); + + snap_array_free(num_snaps, snaps); + + dbus_message_unref(req_msg); + dbus_message_unref(rsp_msg); + + return 0; +} + +static int cdbus_list_confs_pack(DBusMessage **req_msg_out) +{ + DBusMessage *msg; + + msg = dbus_message_new_method_call("org.opensuse.Snapper", + "/org/opensuse/Snapper", + "org.opensuse.Snapper", + "ListConfigs"); + if (NULL == msg) { + fprintf(stderr, "Message Null\n"); + return -ENOMEM; + } + + /* no arguments to append */ + *req_msg_out = msg; + + return 0; +} + +static int conf_struct_unpack(DBusMessageIter *iter, + struct config *conf_out) +{ + int ret; + DBusMessageIter st_iter; + + if (cdbus_type_check(iter, DBUS_TYPE_STRUCT) < 0) { + return -EINVAL; + } + dbus_message_iter_recurse(iter, &st_iter); + + ret = cdbus_type_check_get(&st_iter, DBUS_TYPE_STRING, + &conf_out->name); + if (ret < 0) { + return ret; + } + + dbus_message_iter_next(&st_iter); + ret = cdbus_type_check_get(&st_iter, DBUS_TYPE_STRING, + &conf_out->mnt); + if (ret < 0) { + return ret; + } + + dbus_message_iter_next(&st_iter); + ret = dict_array_unpack(&st_iter, &conf_out->num_attrs, + &conf_out->attrs); + + return ret; +} + +static void conf_array_free(int32_t num_confs, + struct config *confs) +{ + int i; + + for (i = 0; i < num_confs; i++) { + free(confs[i].attrs); + } + free(confs); +} + +static void conf_array_print(int32_t num_confs, + struct config *confs) +{ + int i; + + for (i = 0; i < num_confs; i++) { + printf("name: %s\n" + "mnt: %s\n", + confs[i].name, confs[i].mnt); + dict_array_print(confs[i].num_attrs, confs[i].attrs); + printf("---\n"); + } +} + +static int conf_array_unpack(DBusMessageIter *iter, + uint32_t *num_confs_out, + struct config **confs_out) +{ + uint32_t num_confs; + int ret; + struct config *confs = NULL; + DBusMessageIter array_iter; + + + if (cdbus_type_check(iter, DBUS_TYPE_ARRAY) < 0) { + return -EINVAL; + } + dbus_message_iter_recurse(iter, &array_iter); + + num_confs = 0; + while (dbus_message_iter_get_arg_type(&array_iter) + != DBUS_TYPE_INVALID) { + num_confs++; + confs = realloc(confs, sizeof(struct config) * num_confs); + if (confs == NULL) + abort(); + + ret = conf_struct_unpack(&array_iter, &confs[num_confs - 1]); + if (ret < 0) { + free(confs); + return ret; + } + dbus_message_iter_next(&array_iter); + } + + *num_confs_out = num_confs; + *confs_out = confs; + + return 0; +} + +static int cdbus_list_confs_unpack(DBusConnection *conn, + DBusMessage *rsp_msg, + uint32_t *num_confs_out, + struct config **confs_out) +{ + int ret; + DBusMessageIter iter; + int msg_type; + uint32_t num_confs; + struct config *confs; + const char *sig; + + msg_type = dbus_message_get_type(rsp_msg); + if (msg_type == DBUS_MESSAGE_TYPE_ERROR) { + fprintf(stderr, "list_confs error response: %s\n", + dbus_message_get_error_name(rsp_msg)); + return -EINVAL; + } + + if (msg_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) { + fprintf(stderr, "unexpected list_confs ret type: %d\n", + msg_type); + return -EINVAL; + } + + sig = dbus_message_get_signature(rsp_msg); + if ((sig == NULL) + || (strcmp(sig, CDBUS_SIG_LIST_CONFS_RSP) != 0)) { + fprintf(stderr, "bad list confs response sig: %s, " + "expected: %s\n", + (sig ? sig : "NULL"), CDBUS_SIG_LIST_CONFS_RSP); + return -EINVAL; + } + + if (!dbus_message_iter_init(rsp_msg, &iter)) { + /* FIXME return empty? */ + fprintf(stderr, "Message has no arguments!\n"); + return -EINVAL; + } + + ret = conf_array_unpack(&iter, &num_confs, &confs); + if (ret < 0) { + fprintf(stderr, "failed to unpack conf array\n"); + return -EINVAL; + } + + *num_confs_out = num_confs; + *confs_out = confs; + + return 0; +} + +static int cdbus_list_confs_call(DBusConnection *conn) +{ + int ret; + DBusMessage *req_msg; + DBusMessage *rsp_msg; + DBusPendingCall *pending; + uint32_t num_confs = 0; + struct config *confs = NULL; + const char *sig; + + ret = cdbus_list_confs_pack(&req_msg); + if (ret < 0) { + fprintf(stderr, "failed to pack list confs request\n"); + return ret; + } + + ret = cdbus_msg_send(conn, req_msg, &pending); + if (ret < 0) { + dbus_message_unref(req_msg); + return ret; + } + + ret = cdbus_msg_recv(conn, pending, &rsp_msg); + if (ret < 0) { + dbus_message_unref(req_msg); + dbus_pending_call_unref(pending); + return ret; + } + + sig = dbus_message_get_signature(rsp_msg); + if ((sig == NULL) + || (strcmp(sig, CDBUS_SIG_LIST_CONFS_RSP) != 0)) { + fprintf(stderr, "bad list confs response sig: %s, " + "expected: %s\n", + (sig ? sig : "NULL"), CDBUS_SIG_LIST_CONFS_RSP); + dbus_message_unref(req_msg); + dbus_message_unref(rsp_msg); + return -EINVAL; + } + + ret = cdbus_list_confs_unpack(conn, rsp_msg, + &num_confs, &confs); + if (ret < 0) { + fprintf(stderr, "failed to unpack list confs response\n"); + dbus_message_unref(req_msg); + dbus_message_unref(rsp_msg); + return ret; + } + + conf_array_print(num_confs, confs); + + conf_array_free(num_confs, confs); + + dbus_message_unref(req_msg); + dbus_message_unref(rsp_msg); + + return 0; +} + +int main(int argc, char **argv) +{ + DBusConnection *conn; + int ret = -EINVAL; + + if (argc != 2) { + fprintf(stderr, "Syntax: %s \n", + argv[0]); + return -EINVAL; + } + + conn = cdbus_conn(); + if (conn == NULL) { + fprintf(stderr, "connect failed\n"); + return -ENOMEM; + } + + if (!strcmp(argv[1], "list_confs")) { + ret = cdbus_list_confs_call(conn); + } else if (!strcmp(argv[1], "list_snaps")) { + ret = cdbus_list_snaps_call(conn); + } else { + fprintf(stderr, "Syntax: %s \n", + argv[0]); + ret = -EINVAL; + goto err_conn_close; + } + + if (ret < 0) { + fprintf(stderr, "%s call failed: %s\n", + argv[1], strerror(-ret)); + } + +err_conn_close: + dbus_connection_unref(conn); + + return ret; +} diff --git a/examples/Makefile.am b/examples/Makefile.am index f0fe1b80..526c1852 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -2,6 +2,8 @@ # Makefile.am for snapper/examples # +SUBDIRS = C + INCLUDES = -I$(top_srcdir) LDADD = ../snapper/libsnapper.la