glib/gio/gsettings-tool.c
Ryan Lortie 3160bcad6a GDBusConnection.call(): add 'reply_type' argument
This allows the caller to specify the reply type that they are expecting
for this call.  If the reply comes back with the wrong type, GDBus will
generate an appropriate error internally.

  - add a GVariantType * argument to g_dbus_connection_call() and
    _call_sync().

  - move the internal API for computing message types from introspection
    data to be based on GVariantType instead of strings.  Update users
    of this code.

  - have GDBusProxy pass this calculated GVariantType into
    g_dbus_connection_call().  Remove the checks done in GDBusProxy.

  - Update other users of the code (test cases, gdbus-tool, GSettings
    tool, etc).  In some cases, remove redundant checks; in some other
    cases, we are fixing bugs because no checking was done where it
    should have been.

Closes bug #619391.
2010-05-24 17:00:04 -04:00

405 lines
10 KiB
C

/* GLIB - Library of useful routines for C programming
* Copyright (C) 2010 Red Hat, Inc.
*
* 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 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: Matthias Clasen
*/
#include "config.h"
#include <locale.h>
#include <gi18n.h>
#include <gio.h>
static gint
usage (gint *argc,
gchar **argv[],
gboolean use_stdout)
{
GOptionContext *context;
gchar *s;
g_set_prgname (g_path_get_basename ((*argv)[0]));
context = g_option_context_new (_("COMMAND"));
g_option_context_set_help_enabled (context, FALSE);
s = g_strdup_printf (
_("Commands:\n"
" help Show this information\n"
" get Get the value of a key\n"
" set Set the value of a key\n"
" monitor Monitor a key for changes\n"
" writable Check if a key is writable\n"
"\n"
"Use '%s COMMAND --help' to get help for individual commands.\n"),
g_get_prgname ());
g_option_context_set_description (context, s);
g_free (s);
s = g_option_context_get_help (context, FALSE, NULL);
if (use_stdout)
g_print ("%s", s);
else
g_printerr ("%s", s);
g_free (s);
g_option_context_free (context);
return use_stdout ? 0 : 1;
}
static void
remove_arg (gint num, gint *argc, gchar **argv[])
{
gint n;
g_assert (num <= (*argc));
for (n = num; (*argv)[n] != NULL; n++)
(*argv)[n] = (*argv)[n+1];
(*argv)[n] = NULL;
(*argc) = (*argc) - 1;
}
static void
modify_argv0_for_command (gint *argc,
gchar **argv[],
const gchar *command)
{
gchar *s;
g_assert (g_strcmp0 ((*argv)[1], command) == 0);
remove_arg (1, argc, argv);
s = g_strdup_printf ("%s %s", (*argv)[0], command);
(*argv)[0] = s;
}
static gint
handle_get (gint *argc,
gchar **argv[])
{
gchar *schema;
gchar *path;
gchar *key;
GSettings *settings;
GVariant *v;
GOptionContext *context;
GOptionEntry entries[] = {
{ "path", 'p', 0, G_OPTION_ARG_STRING, &path, N_("Specify the path for the schema"), N_("PATH") },
{ NULL }
};
GError *error;
gint ret = 1;
modify_argv0_for_command (argc, argv, "get");
context = g_option_context_new (_("SCHEMA KEY"));
g_option_context_set_help_enabled (context, FALSE);
g_option_context_set_summary (context, _("Get the value of KEY"));
g_option_context_set_description (context,
_("Arguments:\n"
" SCHEMA The id of the schema\n"
" KEY The name of the key\n"));
g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
path = NULL;
error = NULL;
if (!g_option_context_parse (context, argc, argv, NULL) || *argc != 3)
{
gchar *s;
s = g_option_context_get_help (context, FALSE, NULL);
g_printerr ("%s", s);
g_free (s);
goto out;
}
schema = (*argv)[1];
key = (*argv)[2];
settings = g_settings_new_with_path (schema, path);
v = g_settings_get_value (settings, key);
g_print ("%s\n", g_variant_print (v, FALSE));
g_variant_unref (v);
ret = 0;
out:
g_option_context_free (context);
return ret;
}
static gint
handle_set (gint *argc,
gchar **argv[])
{
gchar *schema;
gchar *path;
gchar *key;
gchar *value;
GSettings *settings;
GVariant *v;
const GVariantType *type;
GOptionContext *context;
GOptionEntry entries[] = {
{ "path", 'p', 0, G_OPTION_ARG_STRING, &path, N_("Specify the path for the schema"), N_("PATH") },
{ NULL }
};
GError *error;
gint ret = 1;
modify_argv0_for_command (argc, argv, "set");
context = g_option_context_new (_("SCHEMA KEY VALUE"));
g_option_context_set_help_enabled (context, FALSE);
g_option_context_set_summary (context, _("Set the value of KEY"));
g_option_context_set_description (context,
_("Arguments:\n"
" SCHEMA The id of the schema\n"
" KEY The name of the key\n"
" VALUE The value to set key to, as a serialized GVariant\n"));
g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
path = NULL;
error = NULL;
if (!g_option_context_parse (context, argc, argv, NULL) || *argc != 4)
{
gchar *s;
s = g_option_context_get_help (context, FALSE, NULL);
g_printerr ("%s", s);
g_free (s);
goto out;
}
schema = (*argv)[1];
key = (*argv)[2];
value = (*argv)[3];
settings = g_settings_new_with_path (schema, path);
v = g_settings_get_value (settings, key);
type = g_variant_get_type (v);
g_variant_unref (v);
error = NULL;
v = g_variant_parse (type, value, NULL, NULL, &error);
if (v == NULL)
{
g_printerr ("%s\n", error->message);
goto out;
}
if (!g_settings_set_value (settings, key, v))
{
g_printerr (_("Key %s is not writable\n"), key);
goto out;
}
ret = 0;
/* XXX: workaround for now
*
* if we exit() so quickly, GDBus may not have had a chance to
* actually send the message (since we're using it async).
*
* GDBusConnection has no API to sync or wait for messages to be sent,
* so we send a meaningless message and wait for the reply to ensure
* that all messages that came before must have been sent.
*/
{
GDBusConnection *session;
session = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
g_dbus_connection_call_sync (session, "org.gtk.DoesNotExist", "/",
"org.gtk.DoesNotExist", "Workaround",
g_variant_new ("()"), NULL, 0, -1, NULL, NULL);
}
out:
g_option_context_free (context);
return ret;
}
static gint
handle_writable (gint *argc,
gchar **argv[])
{
gchar *schema;
gchar *path;
gchar *key;
GSettings *settings;
GOptionContext *context;
GOptionEntry entries[] = {
{ "path", 'p', 0, G_OPTION_ARG_STRING, &path, N_("Specify the path for the schema"), N_("PATH") },
{ NULL }
};
GError *error;
gint ret = 1;
modify_argv0_for_command (argc, argv, "writable");
context = g_option_context_new (_("SCHEMA KEY"));
g_option_context_set_help_enabled (context, FALSE);
g_option_context_set_summary (context, _("Find out whether KEY is writable"));
g_option_context_set_description (context,
_("Arguments:\n"
" SCHEMA The id of the schema\n"
" KEY The name of the key\n"));
g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
path = NULL;
error = NULL;
if (!g_option_context_parse (context, argc, argv, NULL) || *argc != 3)
{
gchar *s;
s = g_option_context_get_help (context, FALSE, NULL);
g_printerr ("%s", s);
g_free (s);
goto out;
}
schema = (*argv)[1];
key = (*argv)[2];
settings = g_settings_new_with_path (schema, path);
if (g_settings_is_writable (settings, key))
g_print ("true\n");
else
g_print ("false\n");
ret = 0;
out:
g_option_context_free (context);
return ret;
}
static void
key_changed (GSettings *settings,
const gchar *key)
{
GVariant *v;
gchar *value;
v = g_settings_get_value (settings, key);
value = g_variant_print (v, FALSE);
g_print ("%s\n", value);
g_free (value);
g_variant_unref (v);
}
static gint
handle_monitor (gint *argc,
gchar **argv[])
{
gchar *schema;
gchar *path;
gchar *key;
GSettings *settings;
gchar *detailed_signal;
GMainLoop *loop;
GOptionContext *context;
GOptionEntry entries[] = {
{ "path", 'p', 0, G_OPTION_ARG_STRING, &path, N_("Specify the path for the schema"), N_("PATH") },
{ NULL }
};
GError *error;
gint ret = 1;
modify_argv0_for_command (argc, argv, "monitor");
context = g_option_context_new (_("SCHEMA KEY"));
g_option_context_set_help_enabled (context, FALSE);
g_option_context_set_summary (context,
_("Monitor KEY for changes and print the changed values.\n"
"Monitoring will continue until the process is terminated."));
g_option_context_set_description (context,
_("Arguments:\n"
" SCHEMA The id of the schema\n"
" KEY The name of the key\n"));
g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
path = NULL;
error = NULL;
if (!g_option_context_parse (context, argc, argv, NULL) || *argc != 3)
{
gchar *s;
s = g_option_context_get_help (context, FALSE, NULL);
g_printerr ("%s", s);
g_free (s);
goto out;
}
schema = (*argv)[1];
key = (*argv)[2];
settings = g_settings_new_with_path (schema, path);
detailed_signal = g_strdup_printf ("changed::%s", key);
g_signal_connect (settings, detailed_signal,
G_CALLBACK (key_changed), NULL);
loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (loop);
g_main_loop_unref (loop);
out:
g_option_context_free (context);
return ret;
}
int
main (int argc, char *argv[])
{
gboolean ret = 1;
setlocale (LC_ALL, "");
g_type_init ();
if (argc < 2)
ret = usage (&argc, &argv, FALSE);
else if (g_strcmp0 (argv[1], "help") == 0)
ret = usage (&argc, &argv, TRUE);
else if (g_strcmp0 (argv[1], "get") == 0)
ret = handle_get (&argc, &argv);
else if (g_strcmp0 (argv[1], "set") == 0)
ret = handle_set (&argc, &argv);
else if (g_strcmp0 (argv[1], "monitor") == 0)
ret = handle_monitor (&argc, &argv);
else if (g_strcmp0 (argv[1], "writable") == 0)
ret = handle_writable (&argc, &argv);
else
{
g_printerr (_("Unknown command '%s'\n"), argv[1]);
ret = usage (&argc, &argv, FALSE);
}
return ret;
}