From: David Zeuthen Date: Sun, 21 Jun 2009 23:07:20 +0000 (-0400) Subject: Add a way to spin down drives X-Git-Tag: upstream/2.1.2~827 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=e24bf4a7522fb1738d37d6b674dc3cab81d078e3;p=platform%2Fupstream%2Fudisks2.git Add a way to spin down drives --- diff --git a/doc/man/DeviceKit-disks.xml b/doc/man/DeviceKit-disks.xml index d903336..3dba0e4 100644 --- a/doc/man/DeviceKit-disks.xml +++ b/doc/man/DeviceKit-disks.xml @@ -92,6 +92,16 @@ + + 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). + + + The device is compatible with flash. diff --git a/doc/man/devkit-disks.xml b/doc/man/devkit-disks.xml index c55b2df..aa38687 100644 --- a/doc/man/devkit-disks.xml +++ b/doc/man/devkit-disks.xml @@ -228,6 +228,46 @@ + + + device_file + + seconds + + + + + Configures disk spindown timeout on device_file to + seconds. + See for important information before using this option. + + + 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. + + + + + + + + + seconds + + + + + Configures disk spindown timeout on all disks that can be spun down to seconds. + See for important information before using this option. + + + 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. + + + + + @@ -238,6 +278,40 @@ + SPINNING DOWN DISKS + + Caution should be exercised when configuring disk spin down. + + + Note that every time a disk is spun down, + the start-stop-count 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 spin-up-time ATA SMART attribute. + + + For maximum compatibility (since disks from WD and others won't + spin down using the STANDBY ATA command), + + devkit-disks-daemon8 + + does all timeout handling on the host side. If a device appears + to be idle (this is determined by looking + at /sys/block/sdX/stat from time to time) + for the requested amount of time, the daemon will spin the disk + down using the STANDBY IMMEDIATE ATA COMMAND or + similar. + + + 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. + + + AUTHOR Written by David Zeuthen david@fubar.dk with diff --git a/policy/org.freedesktop.devicekit.disks.policy.in b/policy/org.freedesktop.devicekit.disks.policy.in index 0227342..446fe7a 100644 --- a/policy/org.freedesktop.devicekit.disks.policy.in +++ b/policy/org.freedesktop.devicekit.disks.policy.in @@ -199,4 +199,14 @@ + + <_description>Set drive spindown timeout + <_message>Authentication is required to configure drive spindown timeout + + no + no + yes + + + diff --git a/src/Makefile.am b/src/Makefile.am index 1a604d2..0e905ef 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -95,6 +95,7 @@ libexec_PROGRAMS += devkit-disks-helper-mkfs \ 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 @@ -147,6 +148,10 @@ devkit_disks_helper_linux_md_check_SOURCES = job-shared.h job-linux-md-check.c 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 diff --git a/src/devkit-disks-daemon.c b/src/devkit-disks-daemon.c index ac098f2..4873955 100644 --- a/src/devkit-disks-daemon.c +++ b/src/devkit-disks-daemon.c @@ -36,6 +36,9 @@ /* 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 @@ -125,6 +128,9 @@ struct DevkitDisksDaemonPrivate GList *polling_inhibitors; GList *inhibitors; + + guint spindown_timeout_id; + GList *spindown_inhibitors; }; static void devkit_disks_daemon_class_init (DevkitDisksDaemonClass *klass); @@ -601,6 +607,10 @@ devkit_disks_daemon_finalize (GObject *object) 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); @@ -657,6 +667,7 @@ device_changed (DevkitDisksDaemon *daemon, GUdevDevice *d, gboolean synthesized) } 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); @@ -722,6 +733,7 @@ device_add (DevkitDisksDaemon *daemon, GUdevDevice *d, gboolean emit_event) 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); } @@ -761,6 +773,7 @@ device_remove (DevkitDisksDaemon *daemon, GUdevDevice *d) g_object_unref (device); devkit_disks_daemon_local_update_poller (daemon); + devkit_disks_daemon_local_update_spindown (daemon); } } @@ -1435,6 +1448,207 @@ devkit_disks_daemon_local_check_auth (DevkitDisksDaemon *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 @@ -1758,3 +1972,126 @@ devkit_disks_daemon_uninhibit (DevkitDisksDaemon *daemon, } /*--------------------------------------------------------------------------------------------------------------*/ + + +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; +} + +/*--------------------------------------------------------------------------------------------------------------*/ diff --git a/src/devkit-disks-daemon.h b/src/devkit-disks-daemon.h index 687a662..0ef00dd 100644 --- a/src/devkit-disks-daemon.h +++ b/src/devkit-disks-daemon.h @@ -121,6 +121,8 @@ void devkit_disks_daemon_local_synthesize_changed (DevkitDisksDae 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); @@ -184,6 +186,15 @@ gboolean devkit_disks_daemon_uninhibit (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__ */ diff --git a/src/devkit-disks-device-private.c b/src/devkit-disks-device-private.c index d78c1b2..351d817 100644 --- a/src/devkit-disks-device-private.c +++ b/src/devkit-disks-device-private.c @@ -733,6 +733,16 @@ devkit_disks_device_set_drive_can_detach (DevkitDisksDevice *device, gboolean va } 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)) diff --git a/src/devkit-disks-device-private.h b/src/devkit-disks-device-private.h index acfdfbf..7706910 100644 --- a/src/devkit-disks-device-private.h +++ b/src/devkit-disks-device-private.h @@ -87,9 +87,18 @@ struct DevkitDisksDevicePrivate /* 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 */ /*************/ @@ -152,6 +161,7 @@ struct DevkitDisksDevicePrivate 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; @@ -282,6 +292,7 @@ void devkit_disks_device_set_drive_media_compatibility (DevkitDisksDevice *devic 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); diff --git a/src/devkit-disks-device.c b/src/devkit-disks-device.c index 6c787a6..4594f31 100644 --- a/src/devkit-disks-device.c +++ b/src/devkit-disks-device.c @@ -70,6 +70,9 @@ static void devkit_disks_device_finalize (GObject *object); 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); @@ -206,6 +209,7 @@ enum 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, @@ -512,6 +516,9 @@ get_property (GObject *object, 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); @@ -982,6 +989,10 @@ devkit_disks_device_class_init (DevkitDisksDeviceClass *klass) 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, @@ -1227,12 +1238,21 @@ devkit_disks_device_finalize (GObject *object) } 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); @@ -2159,6 +2179,34 @@ update_info_drive (DevkitDisksDevice *device) /* ---------------------------------------------------------------------------------------------------- */ +/* 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) @@ -3176,6 +3224,10 @@ update_info (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; @@ -9495,3 +9547,160 @@ devkit_disks_device_drive_poll_media (DevkitDisksDevice *device, 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; +} + +/*--------------------------------------------------------------------------------------------------------------*/ diff --git a/src/devkit-disks-device.h b/src/devkit-disks-device.h index 2700f69..4bf3393 100644 --- a/src/devkit-disks-device.h +++ b/src/devkit-disks-device.h @@ -191,6 +191,15 @@ gboolean devkit_disks_device_drive_detach (DevkitDisksDevice *device, 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__ */ diff --git a/src/devkit-disks-poller.c b/src/devkit-disks-poller.c index fe89ad3..b2049b9 100644 --- a/src/devkit-disks-poller.c +++ b/src/devkit-disks-poller.c @@ -41,6 +41,8 @@ static char **argv_buffer = NULL; static size_t argv_size = 0; #endif +#define POLL_SHOW_DEBUG + static void set_proc_title_init (int argc, char *argv[]) { @@ -110,7 +112,9 @@ devkit_disks_poller_poll_device (const gchar *device_file) /* 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 @@ -167,7 +171,7 @@ poller_have_data (GIOChannel *channel, exit (1); } - again: +read_more: status = g_io_channel_read_line (channel, &line, &line_length, @@ -178,20 +182,21 @@ poller_have_data (GIOChannel *channel, 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) { @@ -209,6 +214,7 @@ poller_have_data (GIOChannel *channel, } g_free (line); + goto read_more; out: /* keep the IOChannel around */ @@ -283,6 +289,7 @@ devkit_disks_poller_set_devices (GList *devices) GList *l; gchar **device_array; guint n; + gchar *joined; gchar *devices_to_poll; static gchar *devices_currently_polled = NULL; @@ -298,16 +305,22 @@ devkit_disks_poller_set_devices (GList *devices) 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); } diff --git a/src/devkit-disks-poller.h b/src/devkit-disks-poller.h index 9d80a38..110ded4 100644 --- a/src/devkit-disks-poller.h +++ b/src/devkit-disks-poller.h @@ -29,7 +29,4 @@ gboolean devkit_disks_poller_setup (int argc, char *argv[]); 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 */ diff --git a/src/job-drive-standby.c b/src/job-drive-standby.c new file mode 100644 index 0000000..56997f9 --- /dev/null +++ b/src/job-drive-standby.c @@ -0,0 +1,103 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * Copyright (C) 2009 David Zeuthen + * + * 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 +#include +#include +#include +#include + +#include +#include + +#include + +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; +} diff --git a/src/org.freedesktop.DeviceKit.Disks.Device.xml b/src/org.freedesktop.DeviceKit.Disks.Device.xml index bd4585c..73e4361 100644 --- a/src/org.freedesktop.DeviceKit.Disks.Device.xml +++ b/src/org.freedesktop.DeviceKit.Disks.Device.xml @@ -1113,6 +1113,93 @@ + + + + + Number of seconds before the drive should be spun down. + + + + + + Options related to setting spindown timeout. Currently no options are recognized. + + + + + + A cookie that can be used in the + DriveUnsetSpindownTimeout() method + to unset the spindown timeout of the device. + + + + + + + Configures spindown timeout for the drive. + Check the + drive-can-spindown + 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(1) man page before using it. + + + + The caller will need one of the following PolicyKit authorizations: + + + org.freedesktop.devicekit.disks.drive-set-spindown + To set spindown timeouts + + + + + if the caller lacks the appropriate PolicyKit authorization + if the operation failed + if an invalid or malformed option was given + + + + + + + + + + + + A cookie obtained from the + DriveSetSpindownTimeout() method. + + + + + + + Unsets spindown timeout for the drive. + + + + The caller will need one of the following PolicyKit authorizations: + + + org.freedesktop.devicekit.disks.drive-set-spindown + To set spindown timeouts + + + + + if the caller lacks the appropriate PolicyKit authorization + if the operation failed + + + + + + @@ -2017,6 +2104,15 @@ is TRUE. + + + 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 + device-is-drive + is TRUE. + + diff --git a/src/org.freedesktop.DeviceKit.Disks.xml b/src/org.freedesktop.DeviceKit.Disks.xml index ba635ff..780d738 100644 --- a/src/org.freedesktop.DeviceKit.Disks.xml +++ b/src/org.freedesktop.DeviceKit.Disks.xml @@ -155,6 +155,90 @@ + + + + + Number of seconds before drives should be spun down. + + + + + + Options related to setting spindown timeouts. Currently no options are recognized. + + + + + + A cookie that can be used in the + DriveUnsetAllSpindownTimeouts() method + to unset the spindown timeout for drives. + + + + + + + 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(1) man page before using it. + + + + The caller will need one of the following PolicyKit authorizations: + + + org.freedesktop.devicekit.disks.drive-set-spindown + To set spindown timeouts + + + + + if the caller lacks the appropriate PolicyKit authorization + if the operation failed + if an invalid or malformed option was given + + + + + + + + + + + + A cookie obtained from the + DriveSetSpindownTimeout() method. + + + + + + + Unsets spindown timeout for the drive. + + + + The caller will need one of the following PolicyKit authorizations: + + + org.freedesktop.devicekit.disks.drive-set-spindown + To set spindown timeouts + + + + + if the caller lacks the appropriate PolicyKit authorization + if the operation failed + + + + + + diff --git a/tools/devkit-disks-bash-completion.sh b/tools/devkit-disks-bash-completion.sh index 662501e..57af088 100644 --- a/tools/devkit-disks-bash-completion.sh +++ b/tools/devkit-disks-bash-completion.sh @@ -22,8 +22,10 @@ __devkit_disks() { 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 } diff --git a/tools/devkit-disks.c b/tools/devkit-disks.c index 836b6e0..5b23520 100644 --- a/tools/devkit-disks.c +++ b/tools/devkit-disks.c @@ -60,6 +60,9 @@ static char *opt_show_info = NULL; 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; @@ -382,6 +385,7 @@ typedef struct 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; @@ -579,6 +583,8 @@ collect_props (const char *key, const GValue *value, DeviceProperties *props) 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); @@ -1089,6 +1095,7 @@ do_show_info (const char *object_path) 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: "); @@ -1438,6 +1445,155 @@ out: /* ---------------------------------------------------------------------------------------------------- */ 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[]) { @@ -1574,6 +1730,9 @@ main (int argc, char **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 }, @@ -1697,6 +1856,15 @@ main (int argc, char **argv) } 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; diff --git a/tools/umount-devkit.c b/tools/umount-devkit.c index 730f26f..74c6cf9 100644 --- a/tools/umount-devkit.c +++ b/tools/umount-devkit.c @@ -63,7 +63,6 @@ do_unmount (const char *object_path, object_path, "org.freedesktop.DeviceKit.Disks.Device"); -try_again: error = NULL; if (!org_freedesktop_DeviceKit_Disks_Device_filesystem_unmount (proxy, (const char **) unmount_options,