diff --git a/doc/man/udisksctl.xml.in b/doc/man/udisksctl.xml.in
index 1d5f0782e..3e90708e3 100644
--- a/doc/man/udisksctl.xml.in
+++ b/doc/man/udisksctl.xml.in
@@ -126,6 +126,17 @@
--no-user-interaction
+
+ udisksctl
+ drive-set-standby
+
+ --object-path OBJECT
+ --block-device DEVICE
+
+ --timeout VALUE
+ --no-user-interaction
+
+
udisksctl
monitor
@@ -382,6 +393,40 @@
+
+
+
+
+ Sets the persistent standby timeout for an ATA drive. This
+ configuration is stored in
+ /etc/udisks2/IDENTIFIER.conf and is
+ applied every time the drive is connected (e.g. at start-up,
+ hotplug, or resume from suspend).
+
+
+
+
+
+
+
+
+ The standby timeout value (0-255). A value of zero means
+ "timeouts are disabled": the device will not automatically
+ enter standby mode. Values from 1 to 240 specify multiples
+ of 5 seconds, yielding timeouts from 5 seconds to 20
+ minutes. Values from 241 to 251 specify from 1 to 11 units
+ of 30 minutes, yielding timeouts from 30 minutes to 5.5
+ hours. A value of 252 signifies a timeout of 21 minutes. A
+ value of 253 sets a vendor-defined timeout period between
+ 8 and 12 hours, and the value 254 is reserved. 255 is
+ interpreted as 21 minutes plus 15 seconds. This is similar
+ to the option in
+ hdparm8.
+
+
+
+
+
diff --git a/tools/udisksctl.c b/tools/udisksctl.c
index 91bbe03a3..a4d16fffc 100644
--- a/tools/udisksctl.c
+++ b/tools/udisksctl.c
@@ -2366,6 +2366,314 @@ handle_command_power_off (gint *argc,
/* ---------------------------------------------------------------------------------------------------- */
+static gchar *opt_set_standby_object_path = NULL;
+static gchar *opt_set_standby_device = NULL;
+static gint opt_set_standby_timeout = -1;
+static gboolean opt_set_standby_no_user_interaction = FALSE;
+
+static const GOptionEntry command_set_standby_entries[] =
+{
+ {
+ "object-path",
+ 'p',
+ 0,
+ G_OPTION_ARG_STRING,
+ &opt_set_standby_object_path,
+ "Object path for drive",
+ NULL
+ },
+ {
+ "block-device",
+ 'b',
+ 0,
+ G_OPTION_ARG_STRING,
+ &opt_set_standby_device,
+ "Device file for drive",
+ NULL
+ },
+ {
+ "timeout",
+ 't',
+ 0,
+ G_OPTION_ARG_INT,
+ &opt_set_standby_timeout,
+ "Standby timeout value (0-255, see udisks(8) for details)",
+ NULL
+ },
+ {
+ "no-user-interaction",
+ 0, /* no short option */
+ 0,
+ G_OPTION_ARG_NONE,
+ &opt_set_standby_no_user_interaction,
+ "Do not authenticate the user if needed",
+ NULL
+ },
+ {
+ NULL
+ }
+};
+
+static gint
+handle_command_set_standby (gint *argc,
+ gchar **argv[],
+ gboolean request_completion,
+ const gchar *completion_cur,
+ const gchar *completion_prev)
+{
+ gint ret;
+ GOptionContext *o;
+ gchar *s;
+ gboolean complete_objects;
+ gboolean complete_devices;
+ GList *l;
+ GList *objects;
+ UDisksObject *object;
+ UDisksDriveAta *ata;
+ guint n;
+ GVariant *options;
+ GVariant *configuration;
+ GVariantBuilder builder;
+ GVariantBuilder conf_builder;
+ GError *error;
+ UDisksDrive *proxy;
+
+ ret = 1;
+ opt_set_standby_object_path = NULL;
+ opt_set_standby_device = NULL;
+ opt_set_standby_timeout = -1;
+ object = NULL;
+ options = NULL;
+ configuration = NULL;
+
+ modify_argv0_for_command (argc, argv, "drive-set-standby");
+
+ o = g_option_context_new (NULL);
+ if (request_completion)
+ g_option_context_set_ignore_unknown_options (o, TRUE);
+ g_option_context_set_help_enabled (o, FALSE);
+ g_option_context_set_summary (o, "Set the persistent standby timeout for a drive.");
+ g_option_context_add_main_entries (o,
+ command_set_standby_entries,
+ NULL /* GETTEXT_PACKAGE*/);
+
+ complete_objects = FALSE;
+ if (request_completion && (g_strcmp0 (completion_prev, "--object-path") == 0 || g_strcmp0 (completion_prev, "-p") == 0 ||
+ g_strcmp0 (completion_cur, "--object-path") == 0 || g_strcmp0 (completion_cur, "-p") == 0))
+ {
+ complete_objects = TRUE;
+ remove_arg ((*argc) - 1, argc, argv);
+ }
+
+ complete_devices = FALSE;
+ if (request_completion && (g_strcmp0 (completion_prev, "--block-device") == 0 || g_strcmp0 (completion_prev, "-b") == 0 ||
+ g_strcmp0 (completion_cur, "--block-device") == 0 || g_strcmp0 (completion_cur, "-b") == 0))
+ {
+ complete_devices = TRUE;
+ remove_arg ((*argc) - 1, argc, argv);
+ }
+
+ if (!g_option_context_parse (o, argc, argv, NULL))
+ {
+ if (!request_completion)
+ {
+ s = g_option_context_get_help (o, FALSE, NULL);
+ g_printerr ("%s", s);
+ g_free (s);
+ goto out;
+ }
+ }
+
+ if (request_completion)
+ {
+ if (!complete_objects && !complete_devices)
+ {
+ list_options (command_set_standby_entries);
+ }
+
+ if (complete_objects)
+ {
+ const gchar *object_path;
+ objects = g_dbus_object_manager_get_objects (udisks_client_get_object_manager (client));
+ for (l = objects; l != NULL; l = l->next)
+ {
+ object = UDISKS_OBJECT (l->data);
+ ata = udisks_object_peek_drive_ata (object);
+ if (ata != NULL)
+ {
+ object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (object));
+ g_assert (g_str_has_prefix (object_path, "/org/freedesktop/UDisks2/"));
+ g_print ("%s \n", object_path + sizeof ("/org/freedesktop/UDisks2/") - 1);
+ }
+ }
+ g_list_free_full (objects, g_object_unref);
+ }
+
+ if (complete_devices)
+ {
+ objects = g_dbus_object_manager_get_objects (udisks_client_get_object_manager (client));
+ for (l = objects; l != NULL; l = l->next)
+ {
+ object = UDISKS_OBJECT (l->data);
+ ata = udisks_object_peek_drive_ata (object);
+ if (ata != NULL)
+ {
+ UDisksBlock *block;
+
+ block = udisks_client_get_block_for_drive (client, udisks_object_peek_drive (object), TRUE);
+ if (block != NULL)
+ {
+ const gchar * const *symlinks;
+
+ g_print ("%s \n", udisks_block_get_device (block));
+ symlinks = udisks_block_get_symlinks (block);
+ for (n = 0; symlinks != NULL && symlinks[n] != NULL; n++)
+ g_print ("%s \n", symlinks[n]);
+ g_object_unref (block);
+ }
+ }
+ }
+ g_list_free_full (objects, g_object_unref);
+ }
+ goto out;
+ }
+
+ /* Validate timeout value */
+ if (opt_set_standby_timeout < 0 || opt_set_standby_timeout > 255)
+ {
+ g_printerr ("Error: --timeout must be between 0 and 255\n");
+ s = g_option_context_get_help (o, FALSE, NULL);
+ g_printerr ("%s", s);
+ g_free (s);
+ goto out;
+ }
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
+ if (opt_set_standby_no_user_interaction)
+ {
+ g_variant_builder_add (&builder,
+ "{sv}",
+ "auth.no_user_interaction", g_variant_new_boolean (TRUE));
+ }
+ options = g_variant_builder_end (&builder);
+ g_variant_ref_sink (options);
+
+ /* Build the configuration dictionary */
+ g_variant_builder_init (&conf_builder, G_VARIANT_TYPE_VARDICT);
+ g_variant_builder_add (&conf_builder,
+ "{sv}",
+ "ata-pm-standby", g_variant_new_int32 (opt_set_standby_timeout));
+ configuration = g_variant_builder_end (&conf_builder);
+ g_variant_ref_sink (configuration);
+
+ if (opt_set_standby_object_path != NULL)
+ {
+ object = lookup_object_by_path (opt_set_standby_object_path);
+ if (object == NULL)
+ {
+ g_printerr ("Error looking up object with path %s\n", opt_set_standby_object_path);
+ goto out;
+ }
+ }
+ else if (opt_set_standby_device != NULL)
+ {
+ UDisksObject *block_object;
+ UDisksDrive *drive;
+
+ block_object = lookup_object_by_device (opt_set_standby_device);
+ if (block_object == NULL)
+ {
+ g_printerr ("Error looking up object for device %s\n", opt_set_standby_device);
+ goto out;
+ }
+ drive = udisks_client_get_drive_for_block (client, udisks_object_peek_block (block_object));
+ if (drive == NULL)
+ {
+ g_printerr ("Error looking up drive for device %s\n", opt_set_standby_device);
+ g_object_unref (block_object);
+ goto out;
+ }
+ object = (UDisksObject *) g_dbus_interface_dup_object (G_DBUS_INTERFACE (drive));
+ if (object == NULL)
+ {
+ g_printerr ("Error looking up object for device %s\n", opt_set_standby_device);
+ g_object_unref (block_object);
+ goto out;
+ }
+ g_object_unref (block_object);
+ }
+ else
+ {
+ s = g_option_context_get_help (o, FALSE, NULL);
+ g_printerr ("%s", s);
+ g_free (s);
+ goto out;
+ }
+
+ if (udisks_object_peek_drive_ata (object) == NULL)
+ {
+ UDisksBlock *block;
+
+ block = udisks_object_peek_block (object);
+ g_printerr ("Device %s is not an ATA device\n",
+ block ? udisks_block_get_device (block) : "(not a block device)");
+ g_object_unref (object);
+ goto out;
+ }
+
+ try_again:
+ error = NULL;
+
+ proxy = udisks_object_peek_drive (object);
+ if (!proxy)
+ {
+ g_printerr ("Error setting standby timeout: dbus interface not supported\n");
+ g_object_unref (object);
+ goto out;
+ }
+
+ if (!udisks_drive_call_set_configuration_sync (proxy,
+ configuration,
+ options,
+ NULL, /* GCancellable */
+ &error))
+ {
+ if (error->domain == UDISKS_ERROR &&
+ error->code == UDISKS_ERROR_NOT_AUTHORIZED_CAN_OBTAIN &&
+ setup_local_polkit_agent ())
+ {
+ g_clear_error (&error);
+ goto try_again;
+ }
+ g_dbus_error_strip_remote_error (error);
+ g_printerr ("Error setting standby timeout: %s (%s, %d)\n",
+ error->message, g_quark_to_string (error->domain), error->code);
+ g_clear_error (&error);
+ g_object_unref (object);
+ goto out;
+ }
+
+ g_print ("Set standby timeout to %d on %s\n",
+ opt_set_standby_timeout,
+ g_dbus_object_get_object_path (G_DBUS_OBJECT (object)));
+ g_object_unref (object);
+
+
+ ret = 0;
+
+ out:
+ if (configuration != NULL)
+ g_variant_unref (configuration);
+ if (options != NULL)
+ g_variant_unref (options);
+ g_option_context_free (o);
+ g_free (opt_set_standby_object_path);
+ g_free (opt_set_standby_device);
+ return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
static gchar *opt_info_object = NULL;
static gchar *opt_info_device = NULL;
static gchar *opt_info_drive = NULL;
@@ -3203,19 +3511,20 @@ usage (gint *argc, gchar **argv[], gboolean use_stdout)
g_option_context_parse (o, argc, argv, NULL);
program_name = g_path_get_basename ((*argv)[0]);
description = g_strdup_printf ("Commands:\n"
- " help Shows this information\n"
- " info Shows information about an object\n"
- " dump Shows information about all objects\n"
- " status Shows high-level status\n"
- " monitor Monitor changes to objects\n"
- " mount Mount a filesystem\n"
- " unmount Unmount a filesystem\n"
- " unlock Unlock an encrypted device\n"
- " lock Lock an encrypted device\n"
- " loop-setup Set-up a loop device\n"
- " loop-delete Delete a loop device\n"
- " power-off Safely power off a drive\n"
- " smart-simulate Set SMART data for a drive\n"
+ " help Shows this information\n"
+ " info Shows information about an object\n"
+ " dump Shows information about all objects\n"
+ " status Shows high-level status\n"
+ " monitor Monitor changes to objects\n"
+ " mount Mount a filesystem\n"
+ " unmount Unmount a filesystem\n"
+ " unlock Unlock an encrypted device\n"
+ " lock Lock an encrypted device\n"
+ " loop-setup Set-up a loop device\n"
+ " loop-delete Delete a loop device\n"
+ " power-off Safely power off a drive\n"
+ " smart-simulate Set SMART data for a drive\n"
+ " drive-set-standby Set persistent standby timeout for a drive\n"
"\n"
"Use \"%s COMMAND --help\" to get help on each command.\n",
program_name);
@@ -3424,6 +3733,15 @@ main (int argc,
completion_prev);
goto out;
}
+ else if (g_strcmp0 (command, "drive-set-standby") == 0)
+ {
+ ret = handle_command_set_standby (&argc,
+ &argv,
+ request_completion,
+ completion_cur,
+ completion_prev);
+ goto out;
+ }
else if (g_strcmp0 (command, "dump") == 0)
{
ret = handle_command_dump (&argc,
@@ -3531,6 +3849,7 @@ main (int argc,
"loop-delete \n"
"power-off \n"
"smart-simulate \n"
+ "drive-set-standby \n"
);
ret = 0;
goto out;