/* * 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 <dbus/dbus.h> #include <stdbool.h> #include <stdint.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #define __STDC_FORMAT_MACROS #include <inttypes.h> #define CDBUS_SIG_LIST_SNAPS_RSP "a(uquxussa{ss})" #define CDBUS_SIG_LIST_CONFS_RSP "a(ssa{ss})" #define CDBUS_SIG_CREATE_SNAP_RSP "u" #define CDBUS_SIG_DEL_SNAPS_RSP "" #define CDBUS_SIG_STRING_DICT "{ss}" struct dict { char *key; char *val; }; struct snap { uint32_t id; uint16_t type; uint32_t pre_id; int64_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) { uint32_t 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_INT64, &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(uint32_t num_snaps, struct snap *snaps) { uint32_t i; for (i = 0; i < num_snaps; i++) { free(snaps[i].user_data); } free(snaps); } static void snap_array_print(uint32_t num_snaps, struct snap *snaps) { uint32_t i; for (i = 0; i < num_snaps; i++) { printf("id: %u\n" "type: %u\n" "pre_id: %u\n" "time: %" PRId64 "\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(uint32_t num_confs, struct config *confs) { uint32_t i; for (i = 0; i < num_confs; i++) { free(confs[i].attrs); } free(confs); } static void conf_array_print(uint32_t num_confs, struct config *confs) { uint32_t 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; } static int cdbus_create_snap_pack(char *snapper_conf, char *desc, uint32_t num_user_data, struct dict *user_data, DBusMessage **req_msg_out) { DBusMessage *msg; DBusMessageIter args; DBusMessageIter array_iter; DBusMessageIter struct_iter; const char *empty = ""; uint32_t i; bool ret; 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 */ "CreateSingleSnapshot"); /* method name */ if (msg == NULL) { fprintf(stderr, "failed to create req msg\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; } if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &desc)) { fprintf(stderr, "Out Of Memory!\n"); return -ENOMEM; } /* cleanup */ if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &empty)) { fprintf(stderr, "Out Of Memory!\n"); return -ENOMEM; } ret = dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, CDBUS_SIG_STRING_DICT, &array_iter); if (!ret) { fprintf(stderr, "failed to open array container\n"); return -ENOMEM; } for (i = 0; i < num_user_data; i++) { ret = dbus_message_iter_open_container(&array_iter, DBUS_TYPE_DICT_ENTRY, NULL, &struct_iter); if (!ret) { fprintf(stderr, "failed to open struct container\n"); return -ENOMEM; } if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &user_data[i].key)) { fprintf(stderr, "Out Of Memory!\n"); return -ENOMEM; } if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &user_data[i].val)) { fprintf(stderr, "Out Of Memory!\n"); return -ENOMEM; } ret = dbus_message_iter_close_container(&array_iter, &struct_iter); if (!ret) { fprintf(stderr, "failed to close struct container\n"); return -ENOMEM; } } dbus_message_iter_close_container(&args, &array_iter); *req_msg_out = msg; return 0; } static int cdbus_create_snap_unpack(DBusConnection *conn, DBusMessage *rsp_msg, uint32_t *snap_id_out) { DBusMessageIter iter; int msg_type; const char *sig; msg_type = dbus_message_get_type(rsp_msg); if (msg_type == DBUS_MESSAGE_TYPE_ERROR) { fprintf(stderr, "create snap error response: %s\n", dbus_message_get_error_name(rsp_msg)); return -EINVAL; } if (msg_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) { fprintf(stderr, "unexpected create snap ret type: %d\n", msg_type); return -EINVAL; } sig = dbus_message_get_signature(rsp_msg); if ((sig == NULL) || (strcmp(sig, CDBUS_SIG_CREATE_SNAP_RSP) != 0)) { fprintf(stderr, "bad create snap response sig: %s, " "expected: %s\n", (sig ? sig : "NULL"), CDBUS_SIG_CREATE_SNAP_RSP); return -EINVAL; } /* read the parameters */ if (!dbus_message_iter_init(rsp_msg, &iter)) { fprintf(stderr, "Message has no arguments!\n"); return -EINVAL; } if (cdbus_type_check_get(&iter, DBUS_TYPE_UINT32, snap_id_out)) { return -EINVAL; } return 0; } static int cdbus_create_snap_call(DBusConnection *conn) { int ret; DBusMessage *req_msg; DBusMessage *rsp_msg; DBusPendingCall *pending; uint32_t snap_id; struct dict user_data[1]; user_data[0].key = "key data"; user_data[0].val = "val data"; ret = cdbus_create_snap_pack("root", "this is a desc", 1, user_data, &req_msg); if (ret < 0) { fprintf(stderr, "failed to pack create snap 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_create_snap_unpack(conn, rsp_msg, &snap_id); if (ret < 0) { fprintf(stderr, "failed to unpack create snap response\n"); dbus_message_unref(req_msg); dbus_message_unref(rsp_msg); return ret; } printf("created new snapshot %u\n", snap_id); dbus_message_unref(req_msg); dbus_message_unref(rsp_msg); return 0; } static int cdbus_del_snap_pack(char *snapper_conf, uint32_t snap_id, DBusMessage **req_msg_out) { DBusMessage *msg; DBusMessageIter args; DBusMessageIter array_iter; bool ret; msg = dbus_message_new_method_call("org.opensuse.Snapper", "/org/opensuse/Snapper", "org.opensuse.Snapper", "DeleteSnapshots"); if (msg == NULL) { fprintf(stderr, "failed to create req msg\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; } ret = dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32_AS_STRING, &array_iter); if (!ret) { fprintf(stderr, "failed to open array container\n"); return -ENOMEM; } if (!dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_UINT32, &snap_id)) { fprintf(stderr, "Out Of Memory!\n"); return -ENOMEM; } dbus_message_iter_close_container(&args, &array_iter); *req_msg_out = msg; return 0; } static int cdbus_del_snap_unpack(DBusConnection *conn, DBusMessage *rsp_msg) { int msg_type; const char *sig; msg_type = dbus_message_get_type(rsp_msg); if (msg_type == DBUS_MESSAGE_TYPE_ERROR) { fprintf(stderr, "del snap error response: %s\n", dbus_message_get_error_name(rsp_msg)); return -EINVAL; } if (msg_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) { fprintf(stderr, "unexpected del snap ret type: %d\n", msg_type); return -EINVAL; } sig = dbus_message_get_signature(rsp_msg); if ((sig == NULL) || (strcmp(sig, CDBUS_SIG_DEL_SNAPS_RSP) != 0)) { fprintf(stderr, "bad del snap response sig: %s, " "expected: %s\n", (sig ? sig : "NULL"), CDBUS_SIG_DEL_SNAPS_RSP); return -EINVAL; } /* no parameters in response */ return 0; } static int cdbus_del_snap_call(DBusConnection *conn, uint32_t snap_id) { int ret; DBusMessage *req_msg; DBusMessage *rsp_msg; DBusPendingCall *pending; ret = cdbus_del_snap_pack("root", snap_id, &req_msg); if (ret < 0) { fprintf(stderr, "failed to pack del snap 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_del_snap_unpack(conn, rsp_msg); if (ret < 0) { fprintf(stderr, "failed to unpack del snap response\n"); dbus_message_unref(req_msg); dbus_message_unref(rsp_msg); return ret; } printf("deleted snapshot %u\n", snap_id); 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 <list_confs | list_snaps | create_snap | del_snap> <arg>\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 if (!strcmp(argv[1], "create_snap")) { ret = cdbus_create_snap_call(conn); } else if (!strcmp(argv[1], "del_snap")) { if (argc < 3) { fprintf(stderr, "del_snap requires a snapshot_id argument\n"); return -EINVAL; } ret = cdbus_del_snap_call(conn, atoi(argv[2])); } else { fprintf(stderr, "Syntax: %s <list_confs | list_snaps | create_snap> | del_snap> <arg>\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; }