Exemple #1
0
bool output::check()
{
	dPrintf("()");
	unsigned int dead = 0;
	bool ret = false;

	for (output_stream_map::iterator iter = output_streams.begin(); iter != output_streams.end(); ++iter) {
		bool streaming = iter->second.check();
		ret |= streaming;
		if (!streaming)
			dead++;
	}
	if (dead) {
		dPrintf("%d dead streams found", dead);
		reclaim_resources();
	}

#if 1//DBG
	map_pidtype pids;
	get_pids(pids);
	if (pids.size()) {
		dPrintf("subscribed to the following pids:");
		for (map_pidtype::const_iterator iter = pids.begin(); iter != pids.end(); ++iter)
			__log_printf(stderr, "%d, ", iter->first);
		__log_printf(stderr, "\n");
	}
#endif
	ringbuffer.check();

	return ret;
}
Exemple #2
0
static gboolean cb_inotify(GIOChannel *ch, GIOCondition condition, gpointer data) {
    char buf[1024];
    gsize length = 0;
    GError *err = NULL;
    GIOStatus stat;
    pid_t recent_pid = 0;
    pid_t pid;
    pid_t tgid, ppid;
    gboolean ret = TRUE;

    if (netlink_proc_listening) {
        nlf_debug("netlink is working: stopping the netlink_fallback module");
        ret = FALSE;
        goto out;
    }

    stat = g_io_channel_read_chars(ch, buf, sizeof(buf), &length, &err);
    if (stat != G_IO_STATUS_NORMAL) {
        g_warning("inotify event read failed (GIOStatus = %d)", stat);
        if (err) {
            g_warning("inotify event read failed (GIOChannelError: %s)", err->message);
            g_error_free(err);
        }
        goto out;
    }

    if (length > 0) {
        if ((recent_pid = get_recent_pid()) == 0) goto out; //recent_pid get failed
        // fixme: PIDs missed if pid_max have just been overflowed, probably not fixable if we want avoid looping over
        // near all processes (as the number of total processes approaches to pid_max)
        for (pid = old_recent_pid + 1; pid <= recent_pid; pid++) {
            // we get both processes (thread leaders) and threads
            if (task_by_tid(pid)) continue;
            // throw away threads
            if (get_pids(pid, &tgid, &ppid)) {
                if (tgid != pid) {
                    nlf_debug("New thread detected (pid=%d, tgid=%d, ppid=%d), discarding it.", pid, tgid, ppid);
                    continue;
                }
                nlf_debug("New process detected (pid=%d, tgid=%d, ppid=%d)", pid, tgid, ppid);
                process_new_delay(pid, ppid);
            }
        }
        old_recent_pid = recent_pid;
    }

out:
    return ret;
}
Exemple #3
0
int
pid_in_proclist(DWORD pid)
{
    DWORD *proclist = NULL;
    DWORD numberOfReturnedPIDs;
    DWORD i;

    proclist = get_pids(&numberOfReturnedPIDs);
    if (NULL == proclist) {
        return -1;
    }

    for (i = 0; i < numberOfReturnedPIDs; i++) {
        if (pid == proclist[i]) {
            free(proclist);
            return 1;
        }
    }

    free(proclist);
    return 0;
}
Exemple #4
0
int main (int argc, char *argv[])
{
    int errors, numsig, pid;
    char *ep, *arg, *p;
    int do_pid, do_kill, check_all;
    int *pids, *ip;

    progname = argv[0];
    if ((p = strrchr(progname, '/')) != NULL)
	    progname = p+1;

    setlocale(LC_ALL, "");
    bindtextdomain(PACKAGE, LOCALEDIR);
    textdomain(PACKAGE);

    numsig = SIGTERM;
    do_pid = (! strcmp (progname, "pid")); 	/* Yecch */
    do_kill = 0;
    check_all = 0;

    /*  loop through the arguments.
	actually, -a is the only option can be used with other options.
	`kill' is basically a one-option-at-most program.  */
    for (argc--, argv++; argc > 0; argc--, argv++) {
	arg = *argv;
	if (*arg != '-') {
	    break;
	}
	if (! strcmp (arg, "--")) {
	    argc--, argv++;
	    break;
	}
	if (! strcmp (arg, "-v") || ! strcmp (arg, "-V") ||
	    ! strcmp (arg, "--version")) {
	    printf(_("%s from %s\n"), progname, PACKAGE_STRING);
	    return 0;
	}
	if (! strcmp (arg, "-a")) {
	    check_all++;
	    continue;
	}
	if (! strcmp (arg, "-l")) {
	    if (argc < 2) {
		printsignals (stdout);
		return 0;
	    }
	    if (argc > 2) {
		return usage (1);
	    }
	    /* argc == 2, accept "kill -l $?" */
	    arg = argv[1];
	    if ((numsig = arg_to_signum (arg, 1)) < 0) {
		fprintf (stderr, _("%s: unknown signal %s\n"), progname, arg);
		return 1;
	    }
	    printsig (numsig);
	    return 0;
	}
	if (! strcmp (arg, "-p")) {
	    do_pid++;
	    if (do_kill)
		return usage (1);
	    continue;
	}
	if (! strcmp (arg, "-s")) {
	    if (argc < 2) {
		return usage (1);
	    }
	    do_kill++;
	    if (do_pid)
		return usage (1);
	    argc--, argv++;
	    arg = *argv;
	    if ((numsig = arg_to_signum (arg, 0)) < 0) {
		nosig (arg);
		return 1;
	    }
	    continue;
	}
	if (! strcmp (arg, "-q")) {
	    if (argc < 2)
		return usage (1);
	    argc--, argv++;
	    arg = *argv;
#ifdef HAVE_SIGQUEUE
	    sigdata.sival_int = strtol_or_err(arg, _("failed to parse sigval"));
	    use_sigval = 1;
#endif
	    continue;
	}
	/*  `arg' begins with a dash but is not a known option.
	    so it's probably something like -HUP, or -1/-n
	    try to deal with it.
	    -n could be signal n, or pid -n (i.e. process group n).
	    In case of doubt POSIX tells us to assume a signal.
	    If a signal has been parsed, assume it's a pid, break */
	if (do_kill)
	  break;
	arg++;
	if ((numsig = arg_to_signum (arg, 0)) < 0) {
	    return usage (1);
	}
	do_kill++;
	if (do_pid)
	    return usage (1);
	continue;
    }

    if (! *argv) {
	return usage (1);
    }
    if (do_pid) {
	numsig = -1;
    }

    /*  we're done with the options.
	the rest of the arguments should be process ids and names.
	kill them.  */
    for (errors = 0; (arg = *argv) != NULL; argv++) {
	pid = strtol (arg, &ep, 10);
	if (! *ep)
	    errors += kill_verbose (arg, pid, numsig);
	else {
	    pids = get_pids (arg, check_all);
	    if (! pids) {
		errors++;
		fprintf (stderr, _("%s: can't find process \"%s\"\n"),
			 progname, arg);
		continue;
	    }
	    for (ip = pids; *ip >= 0; ip++)
		errors += kill_verbose (arg, *ip, numsig);
	    free (pids);
	}
    }
    return (errors);
}
gint
tracker_control_general_run (void)
{
	GError *error = NULL;
	GSList *pids;
	GSList *l;
	gchar *str;
	gpointer verbosity_type_enum_class_pointer = NULL;
	TrackerVerbosity set_log_verbosity_value = TRACKER_VERBOSITY_ERRORS;

	/* Constraints */

	if (kill_option != TERM_NONE && terminate_option != TERM_NONE) {
		g_printerr ("%s\n",
		            _("You can not use the --kill and --terminate arguments together"));
		return EXIT_FAILURE;
	}

	if ((hard_reset || soft_reset) && terminate_option != TERM_NONE) {
		g_printerr ("%s\n",
		            _("You can not use the --terminate with --hard-reset or --soft-reset, --kill is implied"));
		return EXIT_FAILURE;
	}

	if (hard_reset && soft_reset) {
		g_printerr ("%s\n",
		            _("You can not use the --hard-reset and --soft-reset arguments together"));
		return EXIT_FAILURE;
	}

	if (get_log_verbosity && set_log_verbosity) {
		g_printerr ("%s\n",
		            _("You can not use the --get-logging and --set-logging arguments together"));
		return EXIT_FAILURE;
	}

	if (set_log_verbosity) {
		if (g_ascii_strcasecmp (set_log_verbosity, "debug") == 0) {
			set_log_verbosity_value = TRACKER_VERBOSITY_DEBUG;
		} else if (g_ascii_strcasecmp (set_log_verbosity, "detailed") == 0) {
			set_log_verbosity_value = TRACKER_VERBOSITY_DETAILED;
		} else if (g_ascii_strcasecmp (set_log_verbosity, "minimal") == 0) {
			set_log_verbosity_value = TRACKER_VERBOSITY_MINIMAL;
		} else if (g_ascii_strcasecmp (set_log_verbosity, "errors") == 0) {
			set_log_verbosity_value = TRACKER_VERBOSITY_ERRORS;
		} else {
			g_printerr ("%s\n",
			            _("Invalid log verbosity, try 'debug', 'detailed', 'minimal' or 'errors'"));
			return EXIT_FAILURE;
		}
	}

	if (hard_reset || soft_reset) {
		/* Imply --kill */
		kill_option = TERM_ALL;
	}

	if (get_log_verbosity || set_log_verbosity) {
		GType etype;

		/* Since we don't reference this enum anywhere, we do
		 * it here to make sure it exists when we call
		 * g_type_class_peek(). This wouldn't be necessary if
		 * it was a param in a GObject for example.
		 *
		 * This does mean that we are leaking by 1 reference
		 * here and should clean it up, but it doesn't grow so
		 * this is acceptable.
		 */
		etype = tracker_verbosity_get_type ();
		verbosity_type_enum_class_pointer = g_type_class_ref (etype);
	}

	/* Unless we are stopping processes or listing processes,
	 * don't iterate them.
	 */
	if (kill_option != TERM_NONE ||
	    terminate_option != TERM_NONE ||
	    list_processes) {
		guint32 own_pid;
		guint32 own_uid;
		gchar *own_pid_str;

		pids = get_pids ();
		str = g_strdup_printf (g_dngettext (NULL,
		                                    "Found %d PID…",
		                                    "Found %d PIDs…",
		                                    g_slist_length (pids)),
		                       g_slist_length (pids));
		g_print ("%s\n", str);
		g_free (str);

		/* Establish own uid/pid */
		own_pid = (guint32) getpid ();
		own_pid_str = g_strdup_printf ("%d", own_pid);
		own_uid = get_uid_for_pid (own_pid_str, NULL);
		g_free (own_pid_str);

		for (l = pids; l; l = l->next) {
			GError *error = NULL;
			gchar *filename;
			gchar *contents = NULL;
			gchar **strv;
			guint uid;

			uid = get_uid_for_pid (l->data, &filename);

			/* Stat the file and make sure current user == file owner */
			if (uid != own_uid) {
				continue;
			}

			/* Get contents to determine basename */
			if (!g_file_get_contents (filename, &contents, NULL, &error)) {
				str = g_strdup_printf (_("Could not open '%s'"), filename);
				g_printerr ("%s, %s\n",
				            str,
				            error ? error->message : _("no error given"));
				g_free (str);
				g_clear_error (&error);
				g_free (contents);
				g_free (filename);

				continue;
			}

			strv = g_strsplit (contents, "^@", 2);
			if (strv && strv[0]) {
				gchar *basename;

				basename = g_path_get_basename (strv[0]);

				if ((g_str_has_prefix (basename, "tracker") == TRUE ||
				     g_str_has_prefix (basename, "lt-tracker") == TRUE) &&
				    g_str_has_suffix (basename, "-control") == FALSE &&
				    g_str_has_suffix (basename, "-status-icon") == FALSE) {
					pid_t pid;

					pid = atoi (l->data);
					str = g_strdup_printf (_("Found process ID %d for '%s'"), pid, basename);
					g_print ("%s\n", str);
					g_free (str);

					if (terminate_option != TERM_NONE) {
						if ((terminate_option == TERM_STORE &&
						     !g_str_has_suffix (basename, "tracker-store")) ||
						    (terminate_option == TERM_MINERS &&
						     !strstr (basename, "tracker-miner"))) {
							continue;
						}

						if (kill (pid, SIGTERM) == -1) {
							const gchar *errstr = g_strerror (errno);

							str = g_strdup_printf (_("Could not terminate process %d"), pid);
							g_printerr ("  %s, %s\n",
							            str,
							            errstr ? errstr : _("no error given"));
							g_free (str);
						} else {
							str = g_strdup_printf (_("Terminated process %d"), pid);
							g_print ("  %s\n", str);
							g_free (str);
						}
					} else if (kill_option != TERM_NONE) {
						if ((kill_option == TERM_STORE &&
						     !g_str_has_suffix (basename, "tracker-store")) ||
						    (kill_option == TERM_MINERS &&
						     !strstr (basename, "tracker-miner"))) {
							continue;
						}

						if (kill (pid, SIGKILL) == -1) {
							const gchar *errstr = g_strerror (errno);

							str = g_strdup_printf (_("Could not kill process %d"), pid);
							g_printerr ("  %s, %s\n",
							            str,
							            errstr ? errstr : _("no error given"));
							g_free (str);
						} else {
							str = g_strdup_printf (_("Killed process %d"), pid);
							g_print ("  %s\n", str);
							g_free (str);
						}
					}
				}

				g_free (basename);
			}

			g_strfreev (strv);
			g_free (contents);
			g_free (filename);
		}

		g_slist_foreach (pids, (GFunc) g_free, NULL);
		g_slist_free (pids);

		/* If we just wanted to list processes, all done */
		if (list_processes &&
		    terminate_option == TERM_NONE &&
		    kill_option == TERM_NONE) {
			return EXIT_SUCCESS;
		}
	}

	if (hard_reset || soft_reset) {
		guint log_handler_id;
#ifndef DISABLE_JOURNAL
		gchar *rotate_to;
		TrackerDBConfig *db_config;
		gsize chunk_size;
		gint chunk_size_mb;
#endif /* DISABLE_JOURNAL */

		/* Set log handler for library messages */
		log_handler_id = g_log_set_handler (NULL,
		                                    G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL,
		                                    log_handler,
		                                    NULL);

		g_log_set_default_handler (log_handler, NULL);

#ifndef DISABLE_JOURNAL
		db_config = tracker_db_config_new ();

		chunk_size_mb = tracker_db_config_get_journal_chunk_size (db_config);
		chunk_size = (gsize) ((gsize) chunk_size_mb * (gsize) 1024 * (gsize) 1024);
		rotate_to = tracker_db_config_get_journal_rotate_destination (db_config);

		/* This call is needed to set the journal's filename */
		tracker_db_journal_set_rotating ((chunk_size_mb != -1),
		                                 chunk_size, rotate_to);

		g_free (rotate_to);
		g_object_unref (db_config);

#endif /* DISABLE_JOURNAL */

		/* Clean up (select_cache_size and update_cache_size don't matter here) */
		if (!tracker_db_manager_init (TRACKER_DB_MANAGER_REMOVE_ALL,
		                              NULL,
		                              FALSE,
		                              FALSE,
		                              100,
		                              100,
		                              NULL,
		                              NULL,
		                              NULL,
		                              &error)) {

			g_message ("Error initializing database: %s", error->message);
			g_free (error);

			return EXIT_FAILURE;
		}
#ifndef DISABLE_JOURNAL
		tracker_db_journal_init (NULL, FALSE, NULL);
#endif /* DISABLE_JOURNAL */

		tracker_db_manager_remove_all (hard_reset);
		tracker_db_manager_shutdown ();
#ifndef DISABLE_JOURNAL
		tracker_db_journal_shutdown (NULL);
#endif /* DISABLE_JOURNAL */

		/* Unset log handler */
		g_log_remove_handler (NULL, log_handler_id);
	}

	if (remove_config) {
		GMainLoop *main_loop;
		GFile *file;
		TrackerCrawler *crawler;
		const gchar *suffix = ".cfg";
		const gchar *home_conf_dir;
		gchar *path;
		GSList *all, *l;

		crawler = tracker_crawler_new ();
		main_loop = g_main_loop_new (NULL, FALSE);

		g_signal_connect (crawler, "check-file",
		                  G_CALLBACK (crawler_check_file_cb),
		                  &suffix);
		g_signal_connect (crawler, "finished",
		                  G_CALLBACK (crawler_finished_cb),
		                  main_loop);

		/* Go through service files */

		/* Check the default XDG_DATA_HOME location */
		home_conf_dir = g_getenv ("XDG_CONFIG_HOME");

		if (home_conf_dir && tracker_path_has_write_access_or_was_created (home_conf_dir)) {
			path = g_build_path (G_DIR_SEPARATOR_S, home_conf_dir, "tracker", NULL);
		} else {
			home_conf_dir = g_getenv ("HOME");

			if (!home_conf_dir || !tracker_path_has_write_access_or_was_created (home_conf_dir)) {
				home_conf_dir = g_get_home_dir ();
			}
			path = g_build_path (G_DIR_SEPARATOR_S, home_conf_dir, ".config", "tracker", NULL);
		}

		file = g_file_new_for_path (path);
		g_free (path);

		g_print ("%s\n", _("Removing configuration files…"));

		tracker_crawler_start (crawler, file, FALSE);
		g_object_unref (file);

		g_main_loop_run (main_loop);
		g_object_unref (crawler);

		g_print ("%s\n", _("Resetting existing configuration…"));

		all = tracker_gsettings_get_all (NULL);

		if (!all) {
			return EXIT_FAILURE;
		}

		for (l = all; l; l = l->next) {
			ComponentGSettings *c = l->data;
			gchar **keys, **p;

			if (!c) {
				continue;
			}

			g_print ("  %s\n", c->name);

			keys = g_settings_list_keys (c->settings);
			for (p = keys; p && *p; p++) {
				g_print ("    %s\n", *p);
				g_settings_reset (c->settings, *p);
			}

			if (keys) {
				g_strfreev (keys);
			}

			g_settings_apply (c->settings);
		}

		tracker_gsettings_free (all);
	}

	/* Deal with logging changes AFTER the config may have been
	 * reset, this way users can actually use --remove-config with
	 * the --set-logging switch.
	 */
	if (get_log_verbosity) {
		GSList *all;
		gint longest = 0;

		all = tracker_gsettings_get_all (&longest);

		if (!all) {
			return EXIT_FAILURE;
		}

		g_print ("%s:\n", _("Components"));
		tracker_gsettings_print_verbosity (all, longest, TRUE);
		g_print ("\n");

		/* Miners */
		g_print ("%s (%s):\n",
		         _("Miners"),
		         _("Only those with config listed"));
		tracker_gsettings_print_verbosity (all, longest, FALSE);
		g_print ("\n");

		tracker_gsettings_free (all);
	}

	if (set_log_verbosity) {
		GSList *all;
		gchar *str;
		gint longest = 0;

		all = tracker_gsettings_get_all (&longest);

		if (!all) {
			return EXIT_FAILURE;
		}

		str = g_strdup_printf (_("Setting log verbosity for all components to '%s'…"), set_log_verbosity);
		g_print ("%s\n", str);
		g_print ("\n");
		g_free (str);

		tracker_gsettings_set_all (all, set_log_verbosity_value);
		tracker_gsettings_free (all);

		/* We free to make sure we get new settings and that
		 * they're saved properly.
		 */
		all = tracker_gsettings_get_all (&longest);

		if (!all) {
			return EXIT_FAILURE;
		}

		g_print ("%s:\n", _("Components"));
		tracker_gsettings_print_verbosity (all, longest, TRUE);
		g_print ("\n");

		/* Miners */
		g_print ("%s (%s):\n",
		         _("Miners"),
		         _("Only those with config listed"));
		tracker_gsettings_print_verbosity (all, longest, FALSE);
		g_print ("\n");

		tracker_gsettings_free (all);
	}

	if (verbosity_type_enum_class_pointer) {
		g_type_class_unref (verbosity_type_enum_class_pointer);
	}

	if (start) {
		TrackerMinerManager *manager;
		GSList *miners, *l;

		if (hard_reset || soft_reset) {
			g_print ("%s\n", _("Waiting one second before starting miners…"));

			/* Give a second's grace to avoid race conditions */
			g_usleep (G_USEC_PER_SEC);
		}

		g_print ("%s\n", _("Starting miners…"));


		/* Auto-start the miners here */
		manager = tracker_miner_manager_new_full (TRUE, &error);
		if (!manager) {
			g_printerr (_("Could not start miners, manager could not be created, %s"),
			            error ? error->message : "unknown error");
			g_printerr ("\n");
			g_clear_error (&error);
			return EXIT_FAILURE;
		}

		miners = tracker_miner_manager_get_available (manager);

		/* Get the status of all miners, this will start all
		 * miners not already running.
		 */
		for (l = miners; l; l = l->next) {
			const gchar *display_name;
			gdouble progress = 0.0;

			display_name = tracker_miner_manager_get_display_name (manager, l->data);

			if (!tracker_miner_manager_get_status (manager,
			                                       l->data,
			                                       NULL,
			                                       &progress,
			                                       NULL)) {
				g_printerr ("  ✗ %s (%s)\n",
				            display_name,
				            _("perhaps a disabled plugin?"));
			} else {
				g_print ("  ✓ %s\n",
				         display_name);
			}

			g_free (l->data);
		}

		g_slist_free (miners);
		g_object_unref (manager);
	}

	if (backup) {
		GDBusConnection *connection;
		GDBusProxy *proxy;
		GError *error = NULL;
		GVariant *v;
		gchar *uri;

		uri = get_uri_from_arg (backup);

		g_print ("%s\n", _("Backing up database"));
		g_print ("  %s\n", uri);

		connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);

		if (!connection) {
			g_critical ("Could not connect to the D-Bus session bus, %s",
			            error ? error->message : "no error given.");
			g_clear_error (&error);
			g_free (uri);

			return EXIT_FAILURE;
		}

		proxy = g_dbus_proxy_new_sync (connection,
		                               G_DBUS_PROXY_FLAGS_NONE,
		                               NULL,
		                               "org.freedesktop.Tracker1",
		                               "/org/freedesktop/Tracker1/Backup",
		                               "org.freedesktop.Tracker1.Backup",
		                               NULL,
		                               &error);

		if (error) {
			g_critical ("Could not create proxy on the D-Bus session bus, %s",
			            error ? error->message : "no error given.");
			g_clear_error (&error);
			g_free (uri);

			return EXIT_FAILURE;
		}

		/* Backup/Restore can take some time */
		g_dbus_proxy_set_default_timeout (proxy, G_MAXINT);

		v = g_dbus_proxy_call_sync (proxy,
		                            "Save",
		                            g_variant_new ("(s)", uri),
		                            G_DBUS_CALL_FLAGS_NONE,
		                            -1,
		                            NULL,
		                            &error);

		if (proxy) {
			g_object_unref (proxy);
		}

		if (error) {
			g_critical ("Could not backup database, %s",
			            error ? error->message : "no error given.");
			g_clear_error (&error);
			g_free (uri);

			return EXIT_FAILURE;
		}

		if (v) {
			g_variant_unref (v);
		}

		g_free (uri);
	}

	if (restore) {
		GDBusConnection *connection;
		GDBusProxy *proxy;
		GError *error = NULL;
		GVariant *v;
		gchar *uri;

		uri = get_uri_from_arg (restore);

		g_print ("%s\n", _("Restoring database from backup"));
		g_print ("  %s\n", uri);

		connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);

		if (!connection) {
			g_critical ("Could not connect to the D-Bus session bus, %s",
			            error ? error->message : "no error given.");
			g_clear_error (&error);
			g_free (uri);

			return EXIT_FAILURE;
		}

		proxy = g_dbus_proxy_new_sync (connection,
		                               G_DBUS_PROXY_FLAGS_NONE,
		                               NULL,
		                               "org.freedesktop.Tracker1",
		                               "/org/freedesktop/Tracker1/Backup",
		                               "org.freedesktop.Tracker1.Backup",
		                               NULL,
		                               &error);

		if (error) {
			g_critical ("Could not create proxy on the D-Bus session bus, %s",
			            error ? error->message : "no error given.");
			g_clear_error (&error);
			g_free (uri);

			return EXIT_FAILURE;
		}

		/* Backup/Restore can take some time */
		g_dbus_proxy_set_default_timeout (proxy, G_MAXINT);

		v = g_dbus_proxy_call_sync (proxy,
		                            "Restore",
		                            g_variant_new ("(s)", uri),
		                            G_DBUS_CALL_FLAGS_NONE,
		                            -1,
		                            NULL,
		                            &error);

		if (proxy) {
			g_object_unref (proxy);
		}

		if (error) {
			g_critical ("Could not restore database, %s",
			            error ? error->message : "no error given.");
			g_clear_error (&error);
			g_free (uri);

			return EXIT_FAILURE;
		}

		if (v) {
			g_variant_unref (v);
		}

		g_free (uri);
	}

	return EXIT_SUCCESS;
}