mirror of
https://github.com/openSUSE/snapper.git
synced 2026-02-05 15:46:00 +01:00
1069 lines
23 KiB
C
1069 lines
23 KiB
C
/*
|
|
* 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;
|
|
}
|