</para></listitem>
</varlistentry>
<varlistentry>
+ <term><option>ID_DRIVE_CAN_SPINDOWN</option></term>
+ <listitem><para> Whether the device can spin down. It is only meaningful
+ to set this to 0 (to avoid marking a device as being capable of spinning down)
+ since the code for spinning down the device is part of DeviceKit-disks itself.
+ If this property is not set, a heuristic will be used to determine if the
+ drive can spin down (currently only ATA devices, including those USB
+ devices with a SAT layer) can be spun down).
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
<term><option>ID_DRIVE_FLASH</option></term>
<listitem><para>The device is compatible with flash.
</para></listitem>
</varlistentry>
<varlistentry>
+ <term>
+ <option>--set-spindown</option>
+ <arg choice="plain"><replaceable>device_file</replaceable></arg>
+ <option>--spindown-timeout</option>
+ <arg choice="plain"><replaceable>seconds</replaceable></arg>
+ <arg><option>-- program arg ...</option></arg>
+ </term>
+ <listitem>
+ <para>
+ Configures disk spindown timeout on <replaceable>device_file</replaceable> to
+ <replaceable>seconds</replaceable>.
+ See <xref linkend="devkit-disks-spindown"/> for important information before using this option.
+ </para>
+ <para>
+ If no program is given, the spindown time will be used until Ctrl+C is pressed. Otherwise the program is
+ spawned and the the spindown timeout will only be used until the program terminates.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>--set-spindown-all</option>
+ <option>--spindown-timeout</option>
+ <arg choice="plain"><replaceable>seconds</replaceable></arg>
+ <arg><option>-- program arg ...</option></arg>
+ </term>
+ <listitem>
+ <para>
+ Configures disk spindown timeout on all disks that can be spun down to <replaceable>seconds</replaceable>.
+ See <xref linkend="devkit-disks-spindown"/> for important information before using this option.
+ </para>
+ <para>
+ If no program is given, the spindown time will be used until Ctrl+C is pressed. Otherwise the program is
+ spawned and the the spindown timeout will only be used until the program terminates.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>--help</option></term>
<listitem>
<para>
</variablelist>
</refsect1>
+ <refsect1 id="devkit-disks-spindown"><title>SPINNING DOWN DISKS</title>
+ <para>
+ Caution should be exercised when configuring disk spin down.
+ </para>
+ <para>
+ Note that every time a disk is spun down,
+ the <quote>start-stop-count</quote> ATA SMART attribute will
+ increase by 1 and most disks are only good for a limited number
+ (typically 50,000 but it varies by manufacturer and model). In
+ addition, the drive may take as long as 30 seconds to respond to
+ subsequent disk access – most drives are typically faster, see
+ the <quote>spin-up-time</quote> ATA SMART attribute.
+ </para>
+ <para>
+ For maximum compatibility (since disks from WD and others won't
+ spin down using the <quote>STANDBY</quote> ATA command),
+ <citerefentry>
+ <refentrytitle>devkit-disks-daemon</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>
+ does all timeout handling on the host side. If a device appears
+ to be idle (this is determined by looking
+ at <filename>/sys/block/sdX/stat</filename> from time to time)
+ for the requested amount of time, the daemon will spin the disk
+ down using the <quote>STANDBY IMMEDIATE</quote> ATA COMMAND or
+ similar.
+ </para>
+ <para>
+ On the other hand, cautious use (e.g. using conservative
+ timeouts) of the ability to spin down disks, can be a good way
+ to trade power consumption (typically 8 vs 1 Watts for 3.5"
+ drives) and heat emission for a slightly higher latency.
+ </para>
+ </refsect1>
+
<refsect1><title>AUTHOR</title>
<para>
Written by David Zeuthen <email>david@fubar.dk</email> with
</defaults>
</action>
+ <action id="org.freedesktop.devicekit.disks.drive-set-spindown">
+ <_description>Set drive spindown timeout</_description>
+ <_message>Authentication is required to configure drive spindown timeout</_message>
+ <defaults>
+ <allow_any>no</allow_any>
+ <allow_inactive>no</allow_inactive>
+ <allow_active>yes</allow_active>
+ </defaults>
+ </action>
+
</policyconfig>
devkit-disks-helper-ata-smart-selftest \
devkit-disks-helper-drive-detach \
devkit-disks-helper-linux-md-check \
+ devkit-disks-helper-drive-standby \
$(NULL)
libexec_SCRIPTS = devkit-disks-helper-change-luks-password
devkit_disks_helper_linux_md_check_CPPFLAGS = $(AM_CPPFLAGS)
devkit_disks_helper_linux_md_check_LDADD = $(GLIB_LIBS)
+devkit_disks_helper_drive_standby_SOURCES = job-shared.h job-drive-standby.c
+devkit_disks_helper_drive_standby_CPPFLAGS = $(AM_CPPFLAGS) $(SGUTILS_CFLAGS)
+devkit_disks_helper_drive_standby_LDADD = $(SGUTILS_LIBS) $(GLIB_LIBS)
+
# TODO: move to udev
udevhelperdir = $(slashlibdir)/udev
udevhelper_PROGRAMS = devkit-disks-part-id devkit-disks-dm-export devkit-disks-probe-ata-smart
/* delete entries older than five days */
#define ATA_SMART_KEEP_ENTRIES_SECONDS (5*24*60*60)
+/* the poll frequency for IO activity when clients wants to spin down drives */
+#define SPINDOWN_POLL_FREQ_SECONDS 5
+
/* ---------------------------------------------------------------------------------------------------- */
#include <stdlib.h>
GList *polling_inhibitors;
GList *inhibitors;
+
+ guint spindown_timeout_id;
+ GList *spindown_inhibitors;
};
static void devkit_disks_daemon_class_init (DevkitDisksDaemonClass *klass);
g_source_remove (daemon->priv->ata_smart_refresh_timer_id);
}
+ if (daemon->priv->spindown_timeout_id > 0) {
+ g_source_remove (daemon->priv->spindown_timeout_id);
+ }
+
for (l = daemon->priv->polling_inhibitors; l != NULL; l = l->next) {
DevkitDisksInhibitor *inhibitor = DEVKIT_DISKS_INHIBITOR (l->data);
g_signal_handlers_disconnect_by_func (inhibitor, daemon_polling_inhibitor_disconnected_cb, daemon);
} else {
g_print ("**** CHANGED %s\n", native_path);
devkit_disks_daemon_local_update_poller (daemon);
+ devkit_disks_daemon_local_update_spindown (daemon);
}
} else {
g_print ("**** TREATING CHANGE AS ADD %s\n", native_path);
g_signal_emit (daemon, signals[DEVICE_ADDED_SIGNAL], 0, object_path);
}
devkit_disks_daemon_local_update_poller (daemon);
+ devkit_disks_daemon_local_update_spindown (daemon);
} else {
g_print ("**** IGNORING ADD %s\n", native_path);
}
g_object_unref (device);
devkit_disks_daemon_local_update_poller (daemon);
+ devkit_disks_daemon_local_update_spindown (daemon);
}
}
}
/*--------------------------------------------------------------------------------------------------------------*/
+
+#define SYSFS_BLOCK_STAT_MAX_SIZE 256
+
+static void
+issue_standby_child_watch_cb (GPid pid, int status, gpointer user_data)
+{
+ DevkitDisksDevice *device = DEVKIT_DISKS_DEVICE (user_data);
+
+ if (WIFEXITED (status) && WEXITSTATUS (status) == 0) {
+ //g_print ("**** NOTE: standby helper for %s completed successfully\n", device->priv->device_file);
+ } else {
+ g_warning ("standby helper for %s failed with exit code %d (if_exited=%d)\n",
+ device->priv->device_file,
+ WEXITSTATUS (status),
+ WIFEXITED (status));
+ }
+
+ g_object_unref (device);
+}
+
+static void
+issue_standby_to_drive (DevkitDisksDevice *device)
+{
+ GError *error;
+ GPid pid;
+ gchar *argv[3] = {PACKAGE_LIBEXEC_DIR "/devkit-disks-helper-drive-standby",
+ NULL, /* device_file */
+ NULL};
+
+ argv[1] = device->priv->device_file;
+
+ error = NULL;
+ if (!g_spawn_async_with_pipes (NULL,
+ argv,
+ NULL,
+ G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
+ NULL,
+ NULL,
+ &pid,
+ NULL,
+ NULL,
+ NULL,
+ &error)) {
+ g_warning ("Error launching %s: %s", argv[0], error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ g_child_watch_add (pid, issue_standby_child_watch_cb, g_object_ref (device));
+
+ out:
+ ;
+}
+
+static gboolean
+on_spindown_timeout (gpointer data)
+{
+ DevkitDisksDaemon *daemon = DEVKIT_DISKS_DAEMON (data);
+ GHashTableIter hash_iter;
+ DevkitDisksDevice *device;
+ gchar stat_file_path[PATH_MAX];
+ gchar buf[SYSFS_BLOCK_STAT_MAX_SIZE];
+ int fd;
+ time_t now;
+
+ now = time (NULL);
+
+ /* avoid allocating memory since this happens on a timer, e.g. all the FRAKKING time */
+
+ g_hash_table_iter_init (&hash_iter, daemon->priv->map_object_path_to_device);
+ while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &device)) {
+
+ if (!device->priv->device_is_drive ||
+ !device->priv->drive_can_spindown ||
+ device->priv->spindown_timeout == 0)
+ continue;
+
+ g_snprintf (stat_file_path,
+ sizeof stat_file_path,
+ "%s/stat",
+ device->priv->native_path);
+ fd = open (stat_file_path, O_RDONLY);
+ if (fd == -1) {
+ g_warning ("Error opening %s: %m", stat_file_path);
+ } else {
+ ssize_t bytes_read;
+ bytes_read = read (fd, buf, sizeof buf - 1);
+ close (fd);
+ if (bytes_read < 0) {
+ g_warning ("Error reading from %s: %m", stat_file_path);
+ } else {
+ buf[bytes_read] = '\0';
+ if (device->priv->spindown_last_stat == NULL) {
+ /* handle initial time this is set */
+ device->priv->spindown_last_stat = g_new0 (gchar, SYSFS_BLOCK_STAT_MAX_SIZE);
+ memcpy (device->priv->spindown_last_stat, buf, bytes_read + 1);
+ device->priv->spindown_last_stat_time = now;
+ device->priv->spindown_have_issued_standby = FALSE;
+ } else {
+ if (g_strcmp0 (buf, device->priv->spindown_last_stat) == 0) {
+ gint64 idle_secs;
+
+ idle_secs = now - device->priv->spindown_last_stat_time;
+ /* same */
+ if (idle_secs > device->priv->spindown_timeout &&
+ !device->priv->spindown_have_issued_standby) {
+ device->priv->spindown_have_issued_standby = TRUE;
+ g_print ("*** NOTE: issuing STANDBY IMMEDIATE for %s\n",
+ device->priv->device_file);
+
+ /* and, now, DO IT! */
+ issue_standby_to_drive (device);
+ }
+ } else {
+ /* differ */
+ memcpy (device->priv->spindown_last_stat, buf, bytes_read + 1);
+ device->priv->spindown_last_stat_time = now;
+ device->priv->spindown_have_issued_standby = FALSE;
+ //g_print ("*** NOTE: resetting spindown timeout on %s due "
+ // "to IO activity\n",
+ // device->priv->device_file);
+ }
+ }
+ }
+ }
+
+ }
+
+ /* keep timeout */
+ return TRUE;
+}
+
+void
+devkit_disks_daemon_local_update_spindown (DevkitDisksDaemon *daemon)
+{
+ GHashTableIter hash_iter;
+ DevkitDisksDevice *device;
+ GList *l;
+ gboolean watch_for_spindown;
+
+ watch_for_spindown = FALSE;
+ g_hash_table_iter_init (&hash_iter, daemon->priv->map_object_path_to_device);
+ while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &device)) {
+ gint spindown_timeout;
+
+ if (!device->priv->device_is_drive || !device->priv->drive_can_spindown)
+ continue;
+
+ spindown_timeout = G_MAXINT;
+ if (device->priv->spindown_inhibitors == NULL && daemon->priv->spindown_inhibitors == NULL) {
+ /* no inhibitors */
+ device->priv->spindown_timeout = 0;
+ g_free (device->priv->spindown_last_stat);
+ device->priv->spindown_last_stat = NULL;
+ device->priv->spindown_last_stat_time = 0;
+ device->priv->spindown_have_issued_standby = FALSE;
+ } else {
+ /* first go through all inhibitors on the device */
+ for (l = device->priv->spindown_inhibitors; l != NULL; l = l->next) {
+ DevkitDisksInhibitor *inhibitor = DEVKIT_DISKS_INHIBITOR (l->data);
+ gint spindown_timeout_inhibitor;
+
+ spindown_timeout_inhibitor = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (inhibitor),
+ "spindown-timeout-seconds"));
+
+ if (spindown_timeout_inhibitor < spindown_timeout)
+ spindown_timeout = spindown_timeout_inhibitor;
+ }
+
+ /* the all inhibitors on the daemon */
+ for (l = daemon->priv->spindown_inhibitors; l != NULL; l = l->next) {
+ DevkitDisksInhibitor *inhibitor = DEVKIT_DISKS_INHIBITOR (l->data);
+ gint spindown_timeout_inhibitor;
+
+ spindown_timeout_inhibitor = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (inhibitor),
+ "spindown-timeout-seconds"));
+
+ if (spindown_timeout_inhibitor < spindown_timeout)
+ spindown_timeout = spindown_timeout_inhibitor;
+ }
+
+ device->priv->spindown_timeout = spindown_timeout;
+ watch_for_spindown = TRUE;
+ }
+ }
+
+ if (watch_for_spindown) {
+ if (daemon->priv->spindown_timeout_id == 0) {
+ daemon->priv->spindown_timeout_id = g_timeout_add_seconds (SPINDOWN_POLL_FREQ_SECONDS,
+ on_spindown_timeout,
+ daemon);
+ }
+ } else {
+ if (daemon->priv->spindown_timeout_id > 0) {
+ g_source_remove (daemon->priv->spindown_timeout_id);
+ daemon->priv->spindown_timeout_id = 0;
+ }
+ }
+}
+
+/*--------------------------------------------------------------------------------------------------------------*/
/* exported methods */
static void
}
/*--------------------------------------------------------------------------------------------------------------*/
+
+
+static void
+daemon_spindown_inhibitor_disconnected_cb (DevkitDisksInhibitor *inhibitor,
+ DevkitDisksDaemon *daemon)
+{
+ daemon->priv->spindown_inhibitors = g_list_remove (daemon->priv->spindown_inhibitors, inhibitor);
+ g_signal_handlers_disconnect_by_func (inhibitor, daemon_spindown_inhibitor_disconnected_cb, daemon);
+ g_object_unref (inhibitor);
+
+ devkit_disks_daemon_local_update_spindown (daemon);
+}
+
+static void
+devkit_disks_daemon_drive_set_all_spindown_timeouts_authorized_cb (DevkitDisksDaemon *daemon,
+ DevkitDisksDevice *device,
+ DBusGMethodInvocation *context,
+ const gchar *action_id,
+ guint num_user_data,
+ gpointer *user_data_elements)
+{
+ gint timeout_seconds = GPOINTER_TO_INT (user_data_elements[0]);
+ gchar **options = user_data_elements[1];
+ DevkitDisksInhibitor *inhibitor;
+ guint n;
+
+ if (timeout_seconds < 1) {
+ throw_error (context, DEVKIT_DISKS_ERROR_FAILED,
+ "Timeout seconds must be at least 1");
+ goto out;
+ }
+
+ for (n = 0; options[n] != NULL; n++) {
+ const char *option = options[n];
+ throw_error (context,
+ DEVKIT_DISKS_ERROR_INVALID_OPTION,
+ "Unknown option %s", option);
+ goto out;
+ }
+
+ inhibitor = devkit_disks_inhibitor_new (context);
+
+ g_object_set_data (G_OBJECT (inhibitor), "spindown-timeout-seconds", GINT_TO_POINTER (timeout_seconds));
+
+ daemon->priv->spindown_inhibitors = g_list_prepend (daemon->priv->spindown_inhibitors, inhibitor);
+ g_signal_connect (inhibitor, "disconnected", G_CALLBACK (daemon_spindown_inhibitor_disconnected_cb), daemon);
+
+ devkit_disks_daemon_local_update_spindown (daemon);
+
+ dbus_g_method_return (context, devkit_disks_inhibitor_get_cookie (inhibitor));
+
+out:
+ ;
+}
+
+gboolean
+devkit_disks_daemon_drive_set_all_spindown_timeouts (DevkitDisksDaemon *daemon,
+ int timeout_seconds,
+ char **options,
+ DBusGMethodInvocation *context)
+{
+ if (timeout_seconds < 1) {
+ throw_error (context, DEVKIT_DISKS_ERROR_FAILED,
+ "Timeout seconds must be at least 1");
+ goto out;
+ }
+
+ devkit_disks_daemon_local_check_auth (daemon,
+ NULL,
+ "org.freedesktop.devicekit.disks.drive-set-spindown",
+ "DriveSetAllSpindownTimeouts",
+ devkit_disks_daemon_drive_set_all_spindown_timeouts_authorized_cb,
+ context,
+ 2,
+ GINT_TO_POINTER (timeout_seconds), NULL,
+ g_strdupv (options), g_strfreev);
+
+
+ out:
+ return TRUE;
+}
+
+gboolean
+devkit_disks_daemon_drive_unset_all_spindown_timeouts (DevkitDisksDaemon *daemon,
+ char *cookie,
+ DBusGMethodInvocation *context)
+{
+ const gchar *sender;
+ DevkitDisksInhibitor *inhibitor;
+ GList *l;
+
+ sender = dbus_g_method_get_sender (context);
+
+ inhibitor = NULL;
+ for (l = daemon->priv->spindown_inhibitors; l != NULL; l = l->next) {
+ DevkitDisksInhibitor *i = DEVKIT_DISKS_INHIBITOR (l->data);
+
+ if (g_strcmp0 (devkit_disks_inhibitor_get_unique_dbus_name (i), sender) == 0 &&
+ g_strcmp0 (devkit_disks_inhibitor_get_cookie (i), cookie) == 0) {
+ inhibitor = i;
+ break;
+ }
+ }
+
+ if (inhibitor == NULL) {
+ throw_error (context,
+ DEVKIT_DISKS_ERROR_FAILED,
+ "No such spindown configurator");
+ goto out;
+ }
+
+ daemon->priv->spindown_inhibitors = g_list_remove (daemon->priv->spindown_inhibitors, inhibitor);
+ g_object_unref (inhibitor);
+
+ devkit_disks_daemon_local_update_spindown (daemon);
+
+ dbus_g_method_return (context);
+
+ out:
+ return TRUE;
+}
+
+/*--------------------------------------------------------------------------------------------------------------*/
void devkit_disks_daemon_local_update_poller (DevkitDisksDaemon *daemon);
+void devkit_disks_daemon_local_update_spindown (DevkitDisksDaemon *daemon);
+
gboolean devkit_disks_daemon_local_has_polling_inhibitors (DevkitDisksDaemon *daemon);
gboolean devkit_disks_daemon_local_has_inhibitors (DevkitDisksDaemon *daemon);
char *cookie,
DBusGMethodInvocation *context);
+gboolean devkit_disks_daemon_drive_set_all_spindown_timeouts (DevkitDisksDaemon *daemon,
+ int timeout_seconds,
+ char **options,
+ DBusGMethodInvocation *context);
+
+gboolean devkit_disks_daemon_drive_unset_all_spindown_timeouts (DevkitDisksDaemon *daemon,
+ char *cookie,
+ DBusGMethodInvocation *context);
+
G_END_DECLS
#endif /* __DEVKIT_DISKS_DAEMON_H__ */
}
void
+devkit_disks_device_set_drive_can_spindown (DevkitDisksDevice *device, gboolean value)
+{
+ if (G_UNLIKELY (device->priv->drive_can_spindown != value))
+ {
+ device->priv->drive_can_spindown = value;
+ emit_changed (device, "drive_can_spindown");
+ }
+}
+
+void
devkit_disks_device_set_optical_disc_is_blank (DevkitDisksDevice *device, gboolean value)
{
if (G_UNLIKELY (device->priv->optical_disc_is_blank != value))
/* A list of current polling inhibitors (DevkitDisksInhibitor objects) */
GList *polling_inhibitors;
+ /* A list of current spindown configurators (DevkitDisksInhibitor objects) */
+ GList *spindown_inhibitors;
+
/* if non-zero, the id of the idle for emitting a 'change' signal */
guint emit_changed_idle_id;
+ /* used for spindown */
+ gint spindown_timeout;
+ gchar *spindown_last_stat;
+ time_t spindown_last_stat_time;
+ gboolean spindown_have_issued_standby;
+
/**************/
/* properties */
/*************/
char *drive_media;
gboolean drive_is_media_ejectable;
gboolean drive_can_detach;
+ gboolean drive_can_spindown;
gboolean optical_disc_is_blank;
gboolean optical_disc_is_appendable;
void devkit_disks_device_set_drive_media (DevkitDisksDevice *device, const gchar *value);
void devkit_disks_device_set_drive_is_media_ejectable (DevkitDisksDevice *device, gboolean value);
void devkit_disks_device_set_drive_can_detach (DevkitDisksDevice *device, gboolean value);
+void devkit_disks_device_set_drive_can_spindown (DevkitDisksDevice *device, gboolean value);
void devkit_disks_device_set_optical_disc_is_blank (DevkitDisksDevice *device, gboolean value);
void devkit_disks_device_set_optical_disc_is_appendable (DevkitDisksDevice *device, gboolean value);
static void polling_inhibitor_disconnected_cb (DevkitDisksInhibitor *inhibitor,
DevkitDisksDevice *device);
+static void spindown_inhibitor_disconnected_cb (DevkitDisksInhibitor *inhibitor,
+ DevkitDisksDevice *device);
+
static gboolean update_info (DevkitDisksDevice *device);
static void drain_pending_changes (DevkitDisksDevice *device, gboolean force_update);
PROP_DRIVE_MEDIA,
PROP_DRIVE_IS_MEDIA_EJECTABLE,
PROP_DRIVE_CAN_DETACH,
+ PROP_DRIVE_CAN_SPINDOWN,
PROP_OPTICAL_DISC_IS_BLANK,
PROP_OPTICAL_DISC_IS_APPENDABLE,
case PROP_DRIVE_CAN_DETACH:
g_value_set_boolean (value, device->priv->drive_can_detach);
break;
+ case PROP_DRIVE_CAN_SPINDOWN:
+ g_value_set_boolean (value, device->priv->drive_can_spindown);
+ break;
case PROP_OPTICAL_DISC_IS_BLANK:
g_value_set_boolean (value, device->priv->optical_disc_is_blank);
object_class,
PROP_DRIVE_CAN_DETACH,
g_param_spec_boolean ("drive-can-detach", NULL, NULL, FALSE, G_PARAM_READABLE));
+ g_object_class_install_property (
+ object_class,
+ PROP_DRIVE_CAN_SPINDOWN,
+ g_param_spec_boolean ("drive-can-spindown", NULL, NULL, FALSE, G_PARAM_READABLE));
g_object_class_install_property (
object_class,
}
g_list_free (device->priv->polling_inhibitors);
+ for (l = device->priv->spindown_inhibitors; l != NULL; l = l->next) {
+ DevkitDisksInhibitor *inhibitor = DEVKIT_DISKS_INHIBITOR (l->data);
+ g_signal_handlers_disconnect_by_func (inhibitor, spindown_inhibitor_disconnected_cb, device);
+ g_object_unref (inhibitor);
+ }
+ g_list_free (device->priv->spindown_inhibitors);
+
if (device->priv->linux_md_poll_timeout_id > 0)
g_source_remove (device->priv->linux_md_poll_timeout_id);
if (device->priv->emit_changed_idle_id > 0)
g_source_remove (device->priv->emit_changed_idle_id);
+ g_free (device->priv->spindown_last_stat);
+
/* free properties */
g_free (device->priv->device_file);
g_ptr_array_foreach (device->priv->device_file_by_id, (GFunc) g_free, NULL);
/* ---------------------------------------------------------------------------------------------------- */
+/* update drive_can_spindown property */
+static gboolean
+update_info_drive_can_spindown (DevkitDisksDevice *device)
+{
+ gboolean drive_can_spindown;
+
+ /* Right now we only know how to spin down ATA devices (including those USB devices
+ * that can do ATA SMART)
+ *
+ * This would probably also work for SCSI devices (since the helper is doing SCSI
+ * STOP (which translated in libata to ATA's STANDBY IMMEDIATE) - but that needs
+ * testing...
+ */
+ drive_can_spindown = FALSE;
+ if (g_strcmp0 (device->priv->drive_connection_interface, "ata") == 0 ||
+ device->priv->drive_ata_smart_is_available) {
+ drive_can_spindown = TRUE;
+ }
+ if (g_udev_device_has_property (device->priv->d, "ID_DRIVE_CAN_SPINDOWN")) {
+ drive_can_spindown = g_udev_device_get_property_as_boolean (device->priv->d, "ID_DRIVE_CAN_SPINDOWN");
+ }
+ devkit_disks_device_set_drive_can_spindown (device, drive_can_spindown);
+
+ return TRUE;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
/* update device_is_optical_disc and optical_disc_* properties */
static gboolean
update_info_optical_disc (DevkitDisksDevice *device)
if (!update_info_drive_ata_smart (device))
goto out;
+ /* drive_can_spindown property */
+ if (!update_info_drive_can_spindown (device))
+ goto out;
+
/* device_is_system_internal property */
if (!update_info_is_system_internal (device))
goto out;
out:
return TRUE;
}
+
+/*--------------------------------------------------------------------------------------------------------------*/
+
+static void
+spindown_inhibitor_disconnected_cb (DevkitDisksInhibitor *inhibitor,
+ DevkitDisksDevice *device)
+{
+ device->priv->spindown_inhibitors = g_list_remove (device->priv->spindown_inhibitors, inhibitor);
+ g_signal_handlers_disconnect_by_func (inhibitor, spindown_inhibitor_disconnected_cb, device);
+ g_object_unref (inhibitor);
+
+ update_info (device);
+ drain_pending_changes (device, FALSE);
+ devkit_disks_daemon_local_update_spindown (device->priv->daemon);
+}
+
+static void
+devkit_disks_device_drive_set_spindown_timeout_authorized_cb (DevkitDisksDaemon *daemon,
+ DevkitDisksDevice *device,
+ DBusGMethodInvocation *context,
+ const gchar *action_id,
+ guint num_user_data,
+ gpointer *user_data_elements)
+{
+ gint timeout_seconds = GPOINTER_TO_INT (user_data_elements[0]);
+ gchar **options = user_data_elements[1];
+ DevkitDisksInhibitor *inhibitor;
+ guint n;
+
+ if (!device->priv->device_is_drive) {
+ throw_error (context, DEVKIT_DISKS_ERROR_FAILED,
+ "Device is not a drive");
+ goto out;
+ }
+
+ if (!device->priv->drive_can_spindown) {
+ throw_error (context, DEVKIT_DISKS_ERROR_FAILED,
+ "Cannot spindown device");
+ goto out;
+ }
+
+ if (timeout_seconds < 1) {
+ throw_error (context, DEVKIT_DISKS_ERROR_FAILED,
+ "Timeout seconds must be at least 1");
+ goto out;
+ }
+
+ for (n = 0; options[n] != NULL; n++) {
+ const char *option = options[n];
+ throw_error (context,
+ DEVKIT_DISKS_ERROR_INVALID_OPTION,
+ "Unknown option %s", option);
+ goto out;
+ }
+
+ inhibitor = devkit_disks_inhibitor_new (context);
+
+ g_object_set_data (G_OBJECT (inhibitor), "spindown-timeout-seconds", GINT_TO_POINTER (timeout_seconds));
+
+ device->priv->spindown_inhibitors = g_list_prepend (device->priv->spindown_inhibitors, inhibitor);
+ g_signal_connect (inhibitor, "disconnected", G_CALLBACK (spindown_inhibitor_disconnected_cb), device);
+
+ update_info (device);
+ drain_pending_changes (device, FALSE);
+ devkit_disks_daemon_local_update_spindown (device->priv->daemon);
+
+ dbus_g_method_return (context, devkit_disks_inhibitor_get_cookie (inhibitor));
+
+out:
+ ;
+}
+
+gboolean
+devkit_disks_device_drive_set_spindown_timeout (DevkitDisksDevice *device,
+ int timeout_seconds,
+ char **options,
+ DBusGMethodInvocation *context)
+{
+ if (!device->priv->device_is_drive) {
+ throw_error (context, DEVKIT_DISKS_ERROR_FAILED,
+ "Device is not a drive");
+ goto out;
+ }
+
+ if (!device->priv->drive_can_spindown) {
+ throw_error (context, DEVKIT_DISKS_ERROR_FAILED,
+ "Cannot spindown device");
+ goto out;
+ }
+
+ if (timeout_seconds < 1) {
+ throw_error (context, DEVKIT_DISKS_ERROR_FAILED,
+ "Timeout seconds must be at least 1");
+ goto out;
+ }
+
+ devkit_disks_daemon_local_check_auth (device->priv->daemon,
+ device,
+ "org.freedesktop.devicekit.disks.drive-set-spindown",
+ "DriveSetSpindownTimeout",
+ devkit_disks_device_drive_set_spindown_timeout_authorized_cb,
+ context,
+ 2,
+ GINT_TO_POINTER (timeout_seconds), NULL,
+ g_strdupv (options), g_strfreev);
+
+
+ out:
+ return TRUE;
+}
+
+
+/*--------------------------------------------------------------------------------------------------------------*/
+
+gboolean
+devkit_disks_device_drive_unset_spindown_timeout (DevkitDisksDevice *device,
+ char *cookie,
+ DBusGMethodInvocation *context)
+{
+ const gchar *sender;
+ DevkitDisksInhibitor *inhibitor;
+ GList *l;
+
+ sender = dbus_g_method_get_sender (context);
+
+ inhibitor = NULL;
+ for (l = device->priv->spindown_inhibitors; l != NULL; l = l->next) {
+ DevkitDisksInhibitor *i = DEVKIT_DISKS_INHIBITOR (l->data);
+
+ if (g_strcmp0 (devkit_disks_inhibitor_get_unique_dbus_name (i), sender) == 0 &&
+ g_strcmp0 (devkit_disks_inhibitor_get_cookie (i), cookie) == 0) {
+ inhibitor = i;
+ break;
+ }
+ }
+
+ if (inhibitor == NULL) {
+ throw_error (context,
+ DEVKIT_DISKS_ERROR_FAILED,
+ "No such spindown configurator");
+ goto out;
+ }
+
+ device->priv->spindown_inhibitors = g_list_remove (device->priv->spindown_inhibitors, inhibitor);
+ g_object_unref (inhibitor);
+
+ update_info (device);
+ drain_pending_changes (device, FALSE);
+ devkit_disks_daemon_local_update_spindown (device->priv->daemon);
+
+ dbus_g_method_return (context);
+
+ out:
+ return TRUE;
+}
+
+/*--------------------------------------------------------------------------------------------------------------*/
char **options,
DBusGMethodInvocation *context);
+gboolean devkit_disks_device_drive_set_spindown_timeout (DevkitDisksDevice *device,
+ int timeout_seconds,
+ char **options,
+ DBusGMethodInvocation *context);
+
+gboolean devkit_disks_device_drive_unset_spindown_timeout (DevkitDisksDevice *device,
+ char *cookie,
+ DBusGMethodInvocation *context);
+
G_END_DECLS
#endif /* __DEVKIT_DISKS_DEVICE_H__ */
static size_t argv_size = 0;
#endif
+#define POLL_SHOW_DEBUG
+
static void
set_proc_title_init (int argc, char *argv[])
{
/* the device file is the canonical device file from udev */
is_cdrom = (g_str_has_prefix (device_file, "/dev/sr") || g_str_has_prefix (device_file, "/dev/scd"));
- //g_debug ("polling '%s'", device_file);
+#ifdef POLL_SHOW_DEBUG
+ g_print ("**** POLLER (%d): polling %s\n", getpid (), device_file);
+#endif
if (is_cdrom) {
/* optical drives need special care
exit (1);
}
- again:
+read_more:
status = g_io_channel_read_line (channel,
&line,
&line_length,
g_error_free (error);
goto out;
}
- if (status == G_IO_STATUS_AGAIN) {
- goto again;
+ if (status == G_IO_STATUS_EOF || status == G_IO_STATUS_AGAIN) {
+ goto out;
}
- //g_debug ("polling process read '%s'", line);
-
- if (line[line_length - 1] == '\n')
- line[line_length - 1] = '\0';
-
- if (line[line_length - 2] == ' ')
- line[line_length - 2] = '\0';
+ g_strstrip (line);
- g_strfreev (poller_devices_to_poll);
- poller_devices_to_poll = g_strsplit (line, " ", 0);
+#ifdef POLL_SHOW_DEBUG
+ g_print ("**** POLLER (%d): polling process read '%s'\n", getpid (), line);
+#endif
+ if (g_str_has_prefix (line, "set-poll:")) {
+ g_strfreev (poller_devices_to_poll);
+ poller_devices_to_poll = g_strsplit (line + strlen ("set-poll:"), " ", 0);
+ } else {
+ g_printerr ("**** POLLER (%d): unknown command '%s'\n", getpid (), line);
+ }
if (g_strv_length (poller_devices_to_poll) == 0) {
if (poller_timeout_id > 0) {
}
g_free (line);
+ goto read_more;
out:
/* keep the IOChannel around */
GList *l;
gchar **device_array;
guint n;
+ gchar *joined;
gchar *devices_to_poll;
static gchar *devices_currently_polled = NULL;
device_array[n] = "\n";
- devices_to_poll = g_strjoinv (" ", device_array);
+ joined = g_strjoinv (" ", device_array);
g_free (device_array);
+ devices_to_poll = g_strconcat ("set-poll:",
+ joined,
+ NULL);
+ g_free (joined);
/* only poke the polling process if the list of currently polled devices change */
if (g_strcmp0 (devices_to_poll, devices_currently_polled) != 0) {
g_free (devices_currently_polled);
devices_currently_polled = devices_to_poll;
+#ifdef POLL_SHOW_DEBUG
+ g_print ("**** POLLER (%d): Sending poll command: '%s'\n", getpid (), devices_currently_polled);
+#endif
write (poller_daemon_write_end_fd, devices_currently_polled, strlen (devices_currently_polled));
- //g_debug ("Want to poll: '%s'", devices_currently_polled);
} else {
g_free (devices_to_poll);
}
void devkit_disks_poller_set_devices (GList *devices);
void devkit_disks_poller_poll_device (const gchar *device_file);
-/* ATA smart polling */
-void devkit_disks_poller_set_ata_smart_devices (GList *devices);
-
#endif /* __DEVKIT_DISKS_POLLER_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * Copyright (C) 2009 David Zeuthen <david@fubar.dk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <scsi/sg_lib.h>
+#include <scsi/sg_cmds.h>
+
+#include <glib.h>
+
+static void
+usage (void)
+{
+ g_printerr ("incorrect usage\n");
+}
+
+int
+main (int argc, char *argv[])
+{
+ int rc;
+ int ret;
+ int sg_fd;
+ const gchar *device;
+
+ ret = 1;
+ sg_fd = -1;
+
+ if (argc != 2)
+ {
+ usage ();
+ goto out;
+ }
+
+ device = argv[1];
+
+ sg_fd = sg_cmds_open_device (device, 1 /* read_only */, 1);
+ if (sg_fd < 0)
+ {
+ g_printerr ("Cannot open %s: %m\n", device);
+ goto out;
+ }
+
+ if ((rc = sg_ll_sync_cache_10 (sg_fd,
+ 0, /* sync_nv */
+ 0, /* immed */
+ 0, /* group */
+ 0, /* lba */
+ 0, /* count */
+ 1, /* noisy */
+ 0 /* verbose */
+ )) != 0)
+ {
+ g_printerr ("Error SYNCHRONIZE CACHE for %s: %s\n", device, safe_strerror (rc));
+ /* this is not a catastrophe, carry on */
+ }
+
+ if (sg_ll_start_stop_unit (sg_fd,
+ 0, /* immed */
+ 0, /* pc_mod__fl_num */
+ 0, /* power_cond */
+ 0, /* noflush__fl */
+ 0, /* loej */
+ 0, /* start */
+ 1, /* noisy */
+ 0 /* verbose */
+ ) != 0)
+ {
+ g_printerr ("Error STOP UNIT for %s: %s\n", device, safe_strerror (rc));
+ goto out;
+ }
+
+ /* OK, close the device */
+ sg_cmds_close_device (sg_fd);
+ sg_fd = -1;
+
+ ret = 0;
+
+ out:
+ if (sg_fd > 0)
+ sg_cmds_close_device (sg_fd);
+ return ret;
+}
<!-- ************************************************************ -->
+ <method name="DriveSetSpindownTimeout">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="timeout_seconds" direction="in" type="i">
+ <doc:doc><doc:summary>
+ Number of seconds before the drive should be spun down.
+ </doc:summary></doc:doc>
+ </arg>
+
+ <arg name="options" direction="in" type="as">
+ <doc:doc><doc:summary>
+ Options related to setting spindown timeout. Currently no options are recognized.
+ </doc:summary></doc:doc>
+ </arg>
+
+ <arg name="cookie" direction="out" type="s">
+ <doc:doc><doc:summary>
+ A cookie that can be used in the
+ <doc:ref type="method" to="Device.DriveUnsetSpindownTimeout">DriveUnsetSpindownTimeout()</doc:ref> method
+ to unset the spindown timeout of the device.
+ </doc:summary></doc:doc>
+ </arg>
+
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Configures spindown timeout for the drive.
+ Check the
+ <doc:ref type="property" to="Device:drive-can-spindown">drive-can-spindown</doc:ref>
+ property before attempting to invoke this method.
+ Caution should be exercised when using this method, see
+ the SPINNING DOWN DISKS section in the
+ devkit-disks<doc:tt>(1)</doc:tt> man page before using it.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller will need one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.devicekit.disks.drive-set-spindown</doc:term>
+ <doc:definition>To set spindown timeouts</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="&ERROR_NOT_AUTHORIZED;">if the caller lacks the appropriate PolicyKit authorization</doc:error>
+ <doc:error name="&ERROR_FAILED;">if the operation failed</doc:error>
+ <doc:error name="&ERROR_INVALID_OPTION;">if an invalid or malformed option was given</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <!-- ************************************************************ -->
+
+ <method name="DriveUnsetSpindownTimeout">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+
+ <arg name="cookie" direction="in" type="s">
+ <doc:doc><doc:summary>
+ A cookie obtained from the
+ <doc:ref type="method" to="Device.DriveSetSpindownTimeout">DriveSetSpindownTimeout()</doc:ref> method.
+ </doc:summary></doc:doc>
+ </arg>
+
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Unsets spindown timeout for the drive.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller will need one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.devicekit.disks.drive-set-spindown</doc:term>
+ <doc:definition>To set spindown timeouts</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="&ERROR_NOT_AUTHORIZED;">if the caller lacks the appropriate PolicyKit authorization</doc:error>
+ <doc:error name="&ERROR_FAILED;">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <!-- ************************************************************ -->
+
<method name="DriveAtaSmartRefreshData">
<annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
<arg name="options" direction="in" type="as">
is TRUE.
</doc:para></doc:description></doc:doc>
</property>
+ <property name="drive-can-spindown" type="b" access="read">
+ <doc:doc><doc:description><doc:para>
+ TRUE only if the drive is capable of being put into
+ a standby mode (typically powering down the spindle motor).
+ This property is only valid if
+ <doc:ref type="property" to="Device:device-is-drive">device-is-drive</doc:ref>
+ is TRUE.
+ </doc:para></doc:description></doc:doc>
+ </property>
<property name="optical-disc-is-blank" type="b" access="read">
<doc:doc><doc:description><doc:para>
<!-- ************************************************************ -->
+ <method name="DriveSetAllSpindownTimeouts">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="timeout_seconds" direction="in" type="i">
+ <doc:doc><doc:summary>
+ Number of seconds before drives should be spun down.
+ </doc:summary></doc:doc>
+ </arg>
+
+ <arg name="options" direction="in" type="as">
+ <doc:doc><doc:summary>
+ Options related to setting spindown timeouts. Currently no options are recognized.
+ </doc:summary></doc:doc>
+ </arg>
+
+ <arg name="cookie" direction="out" type="s">
+ <doc:doc><doc:summary>
+ A cookie that can be used in the
+ <doc:ref type="method" to="Drive.DriveUnsetAllSpindownTimeouts">DriveUnsetAllSpindownTimeouts()</doc:ref> method
+ to unset the spindown timeout for drives.
+ </doc:summary></doc:doc>
+ </arg>
+
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Configures spindown timeout for all drives capable of being spun down.
+ Caution should be exercised when using this method, see
+ the SPINNING DOWN DISKS section in the
+ devkit-disks<doc:tt>(1)</doc:tt> man page before using it.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller will need one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.devicekit.disks.drive-set-spindown</doc:term>
+ <doc:definition>To set spindown timeouts</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="&ERROR_NOT_AUTHORIZED;">if the caller lacks the appropriate PolicyKit authorization</doc:error>
+ <doc:error name="&ERROR_FAILED;">if the operation failed</doc:error>
+ <doc:error name="&ERROR_INVALID_OPTION;">if an invalid or malformed option was given</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <!-- ************************************************************ -->
+
+ <method name="DriveUnsetAllSpindownTimeouts">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+
+ <arg name="cookie" direction="in" type="s">
+ <doc:doc><doc:summary>
+ A cookie obtained from the
+ <doc:ref type="method" to="Device.DriveSetSpindownTimeout">DriveSetSpindownTimeout()</doc:ref> method.
+ </doc:summary></doc:doc>
+ </arg>
+
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Unsets spindown timeout for the drive.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller will need one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.devicekit.disks.drive-set-spindown</doc:term>
+ <doc:definition>To set spindown timeouts</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="&ERROR_NOT_AUTHORIZED;">if the caller lacks the appropriate PolicyKit authorization</doc:error>
+ <doc:error name="&ERROR_FAILED;">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <!-- ************************************************************ -->
+
<method name="LinuxMdStart">
<annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
<arg name="components" direction="in" type="ao">
COMPREPLY=($(compgen -W "$(devkit-disks --enumerate-device-files)" -- $cur))
elif [ "${COMP_WORDS[$(($COMP_CWORD - 1))]}" = "--ata-smart-simulate" ] ; then
_filedir || return 0
+ elif [ "${COMP_WORDS[$(($COMP_CWORD - 1))]}" = "--set-spindown" ] ; then
+ COMPREPLY=($(compgen -W "$(devkit-disks --enumerate-device-files)" -- $cur))
else
- COMPREPLY=($(IFS=: compgen -W "--dump:--inhibit-polling:--inhibit-all-polling:--enumerate:--enumerate-device-files:--monitor:--monitor-detail:--show-info:--help:--mount:--mount-fstype:--mount-options:--unmount:--unmount-options:--detach:--detach-options:--ata-smart-refresh:--ata-smart-wakeup:--ata-smart-simulate" -- $cur))
+ COMPREPLY=($(IFS=: compgen -W "--dump:--inhibit-polling:--inhibit-all-polling:--enumerate:--enumerate-device-files:--monitor:--monitor-detail:--show-info:--help:--mount:--mount-fstype:--mount-options:--unmount:--unmount-options:--detach:--detach-options:--ata-smart-refresh:--ata-smart-wakeup:--ata-smart-simulate:--set-spindown:--set-spindown-all:--spindown-timeout" -- $cur))
fi
}
static char *opt_inhibit_polling = NULL;
static gboolean opt_inhibit = FALSE;
static gboolean opt_inhibit_all_polling = FALSE;
+static char *opt_drive_spindown = NULL;
+static gboolean opt_drive_spindown_all = FALSE;
+static gint opt_spindown_seconds = 0;
static char *opt_mount = NULL;
static char *opt_mount_fstype = NULL;
static char *opt_mount_options = NULL;
char *drive_media;
gboolean drive_is_media_ejectable;
gboolean drive_can_detach;
+ gboolean drive_can_spindown;
gboolean optical_disc_is_blank;
gboolean optical_disc_is_appendable;
props->drive_is_media_ejectable = g_value_get_boolean (value);
else if (strcmp (key, "drive-can-detach") == 0)
props->drive_can_detach = g_value_get_boolean (value);
+ else if (strcmp (key, "drive-can-spindown") == 0)
+ props->drive_can_spindown = g_value_get_boolean (value);
else if (strcmp (key, "optical-disc-is-blank") == 0)
props->optical_disc_is_blank = g_value_get_boolean (value);
g_print (" revision: %s\n", props->drive_revision);
g_print (" serial: %s\n", props->drive_serial);
g_print (" detachable: %d\n", props->drive_can_detach);
+ g_print (" can spindown: %d\n", props->drive_can_spindown);
g_print (" ejectable: %d\n", props->drive_is_media_ejectable);
g_print (" media: %s\n", props->drive_media);
g_print (" compat: ");
/* ---------------------------------------------------------------------------------------------------- */
static gint
+do_set_spindown (const char *object_path,
+ gint argc,
+ gchar *argv[])
+{
+ char *cookie;
+ DBusGProxy *proxy;
+ GError *error;
+ char **options;
+ gint ret;
+
+ options = NULL;
+ cookie = NULL;
+ ret = 127;
+
+ if (argc > 0 && strcmp (argv[0], "--") == 0) {
+ argv++;
+ argc--;
+ }
+
+ proxy = dbus_g_proxy_new_for_name (bus,
+ "org.freedesktop.DeviceKit.Disks",
+ object_path,
+ "org.freedesktop.DeviceKit.Disks.Device");
+
+ error = NULL;
+ if (!org_freedesktop_DeviceKit_Disks_Device_drive_set_spindown_timeout (proxy,
+ opt_spindown_seconds,
+ (const char **) options,
+ &cookie,
+ &error)) {
+ g_print ("Setting spindown failed: %s\n", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ if (argc == 0) {
+ g_print ("Set spindown on %s to %d seconds. Press Ctrl+C to exit.\n",
+ object_path, opt_spindown_seconds);
+ while (TRUE)
+ sleep (100000000);
+ } else {
+ GError *error;
+ gint exit_status;
+
+ error = NULL;
+ if (!g_spawn_sync (NULL, /* working dir */
+ argv,
+ NULL, /* envp */
+ G_SPAWN_SEARCH_PATH,
+ NULL, /* child_setup */
+ NULL, /* user_data */
+ NULL, /* standard_output */
+ NULL, /* standard_error */
+ &exit_status, /* exit_status */
+ &error)) {
+ g_printerr ("Error launching program: %s\n", error->message);
+ g_error_free (error);
+ ret = 126;
+ goto out;
+ }
+
+ if (WIFEXITED (exit_status))
+ ret = WEXITSTATUS (exit_status);
+ else
+ ret = 125;
+ }
+
+out:
+ g_free (cookie);
+ return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gint
+do_set_spindown_all (gint argc,
+ gchar *argv[])
+{
+ char *cookie;
+ DBusGProxy *proxy;
+ GError *error;
+ char **options;
+ gint ret;
+
+ options = NULL;
+ cookie = NULL;
+ ret = 127;
+
+ if (argc > 0 && strcmp (argv[0], "--") == 0) {
+ argv++;
+ argc--;
+ }
+
+ proxy = dbus_g_proxy_new_for_name (bus,
+ "org.freedesktop.DeviceKit.Disks",
+ "/org/freedesktop/DeviceKit/Disks",
+ "org.freedesktop.DeviceKit.Disks");
+
+ error = NULL;
+ if (!org_freedesktop_DeviceKit_Disks_drive_set_all_spindown_timeouts (proxy,
+ opt_spindown_seconds,
+ (const char **) options,
+ &cookie,
+ &error)) {
+ g_print ("Setting spindown failed: %s\n", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ if (argc == 0) {
+ g_print ("Set spindown for all drives to %d seconds. Press Ctrl+C to exit.\n",
+ opt_spindown_seconds);
+ while (TRUE)
+ sleep (100000000);
+ } else {
+ GError *error;
+ gint exit_status;
+
+ error = NULL;
+ if (!g_spawn_sync (NULL, /* working dir */
+ argv,
+ NULL, /* envp */
+ G_SPAWN_SEARCH_PATH,
+ NULL, /* child_setup */
+ NULL, /* user_data */
+ NULL, /* standard_output */
+ NULL, /* standard_error */
+ &exit_status, /* exit_status */
+ &error)) {
+ g_printerr ("Error launching program: %s\n", error->message);
+ g_error_free (error);
+ ret = 126;
+ goto out;
+ }
+
+ if (WIFEXITED (exit_status))
+ ret = WEXITSTATUS (exit_status);
+ else
+ ret = 125;
+ }
+
+out:
+ g_free (cookie);
+ return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gint
do_inhibit (gint argc,
gchar *argv[])
{
{ "show-info", 0, 0, G_OPTION_ARG_STRING, &opt_show_info, "Show information about a device file", NULL },
{ "inhibit-polling", 0, 0, G_OPTION_ARG_STRING, &opt_inhibit_polling, "Inhibit polling", NULL },
{ "inhibit-all-polling", 0, 0, G_OPTION_ARG_NONE, &opt_inhibit_all_polling, "Inhibit all polling", NULL },
+ { "set-spindown", 0, 0, G_OPTION_ARG_STRING, &opt_drive_spindown, "Set spindown timeout for drive", NULL },
+ { "set-spindown-all", 0, 0, G_OPTION_ARG_NONE, &opt_drive_spindown_all, "Set spindown timeout for all drives", NULL },
+ { "spindown-timeout", 0, 0, G_OPTION_ARG_INT, &opt_spindown_seconds, "Spindown timeout in seconds", NULL },
{ "inhibit", 0, 0, G_OPTION_ARG_NONE, &opt_inhibit, "Inhibit the daemon", NULL },
{ "mount", 0, 0, G_OPTION_ARG_STRING, &opt_mount, "Mount the given device", NULL },
} else if (opt_inhibit_all_polling) {
ret = do_inhibit_all_polling (argc - 1, argv + 1);
goto out;
+ } else if (opt_drive_spindown != NULL) {
+ device_file = device_file_to_object_path (opt_drive_spindown);
+ if (device_file == NULL)
+ goto out;
+ ret = do_set_spindown (device_file, argc - 1, argv + 1);
+ goto out;
+ } else if (opt_drive_spindown_all) {
+ ret = do_set_spindown_all (argc - 1, argv + 1);
+ goto out;
} else if (opt_inhibit) {
ret = do_inhibit (argc - 1, argv + 1);
goto out;
object_path,
"org.freedesktop.DeviceKit.Disks.Device");
-try_again:
error = NULL;
if (!org_freedesktop_DeviceKit_Disks_Device_filesystem_unmount (proxy,
(const char **) unmount_options,