blob: 242ef867d8afacb4de3d4623ed5cf8bb5a059659 [file] [log] [blame]
/*
* cli_dbus.c - Teamd daemon control library D-Bus client
* Copyright (C) 2013-2015 Jiri Pirko <jiri@resnulli.us>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "config.h"
#ifdef ENABLE_DBUS
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <dbus/dbus.h>
#include <teamdctl.h>
#include "teamdctl_private.h"
#include "../teamd/teamd_dbus_common.h"
struct cli_dbus_priv {
DBusConnection *conn;
char *service_name;
};
static int cli_dbus_check_error_msg(struct teamdctl *tdc, DBusMessage *msg)
{
DBusMessageIter args;
dbus_bool_t dbres;
char *param = NULL;
const char *err_msg;
err_msg = dbus_message_get_error_name(msg);
if (!err_msg)
return 0;
err(tdc, "dbus: Error message received: \"%s\"", err_msg);
dbres = dbus_message_iter_init(msg, &args);
if (dbres == TRUE) {
if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) {
err(tdc, "dbus: Received argument is not string as expected.");
return -EINVAL;
}
dbus_message_iter_get_basic(&args, &param);
err(tdc, "dbus: Error message content: \"%s\"", param);
}
return -EINVAL;
}
static int cli_dbus_get_reply_str(struct teamdctl *tdc, char **p_reply,
DBusMessage *msg)
{
DBusMessageIter args;
dbus_bool_t dbres;
char *param = NULL;
dbres = dbus_message_iter_init(msg, &args);
if (dbres == FALSE) {
err(tdc, "Failed, no data received.");
return -EINVAL;
}
if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) {
err(tdc, "dbus: Received argument is not string as expected.");
return -EINVAL;
}
dbus_message_iter_get_basic(&args, &param);
*p_reply = param;
return 0;
}
static int cli_dbus_method_call(struct teamdctl *tdc, const char *method_name,
char **p_reply, void *priv,
const char *fmt, va_list ap)
{
struct cli_dbus_priv *cli_dbus = priv;
char *str;
DBusMessage *msg;
DBusMessageIter iter;
dbus_bool_t dbres;
DBusPendingCall *pending;
char *reply;
int err;
dbg(tdc, "dbus: Calling method \"%s\"", method_name);
msg = dbus_message_new_method_call(cli_dbus->service_name,
TEAMD_DBUS_PATH, TEAMD_DBUS_IFACE,
method_name);
if (!msg) {
err(tdc, "dbus: Failed to create message.");
return -ENOMEM;
}
dbus_message_iter_init_append(msg, &iter);
while (*fmt) {
switch (*fmt++) {
case 's': /* string */
str = va_arg(ap, char *);
dbres = dbus_message_iter_append_basic(&iter,
DBUS_TYPE_STRING,
&str);
if (dbres == FALSE) {
err(tdc, "dbus: Failed to construct message.");
err = -ENOMEM;
goto free_msg;
}
break;
default:
err(tdc, "dbus: Unknown argument type requested.");
err = -EINVAL;
goto free_msg;
}
}
dbres = dbus_connection_send_with_reply(cli_dbus->conn, msg,
&pending, TEAMDCTL_REPLY_TIMEOUT);
if (dbres == FALSE) {
err(tdc, "dbus: Send with reply failed.");
err = -ENOMEM;
goto free_msg;
}
if (!pending) {
err(tdc, "dbus: Pending call not created.");
err = -ENOMEM;
goto free_msg;
}
dbus_pending_call_block(pending);
dbus_message_unref(msg);
msg = dbus_pending_call_steal_reply(pending);
dbus_pending_call_unref(pending);
if (!msg) {
err(tdc, "dbus: Failed to get reply.");
err = -EINVAL;
goto out;
}
err = cli_dbus_check_error_msg(tdc, msg);
if (err)
goto free_msg;
if (p_reply) {
err = cli_dbus_get_reply_str(tdc, &reply, msg);
if (err)
goto free_msg;
reply = strdup(reply);
if (!reply) {
err = -ENOMEM;
goto free_msg;
}
*p_reply = reply;
}
free_msg:
dbus_message_unref(msg);
out:
return err;
}
static int cli_dbus_init(struct teamdctl *tdc, const char *team_name, void *priv)
{
struct cli_dbus_priv *cli_dbus = priv;
DBusError error;
int ret;
int err;
ret = asprintf(&cli_dbus->service_name, TEAMD_DBUS_SERVICE ".%s",
team_name);
if (ret == -1)
return -errno;
err = -EINVAL;
dbus_error_init(&error);
if (!dbus_validate_bus_name(cli_dbus->service_name, &error)) {
err(tdc, "dbus: Could not validate bus name: %s - %s",
error.name, error.message);
goto free_service_name;
}
cli_dbus->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
if (!cli_dbus->conn) {
err(tdc, "dbus: Could not acquire the system bus: %s - %s",
error.name, error.message);
goto free_service_name;
}
err = 0;
goto free_error;
free_service_name:
free(cli_dbus->service_name);
free_error:
dbus_error_free(&error);
return err;
}
void cli_dbus_fini(struct teamdctl *tdc, void *priv)
{
struct cli_dbus_priv *cli_dbus = priv;
free(cli_dbus->service_name);
dbus_connection_unref(cli_dbus->conn);
}
static const struct teamdctl_cli cli_dbus = {
.name = "dbus",
.init = cli_dbus_init,
.fini = cli_dbus_fini,
.test_method_call_required = true,
.method_call = cli_dbus_method_call,
.priv_size = sizeof(struct cli_dbus_priv),
};
const struct teamdctl_cli *teamdctl_cli_dbus_get(void)
{
return &cli_dbus;
}
#endif /* ENABLE_DBUS */